Index: lams_tool_pixlr/src/java/org/lamsfoundation/lams/tool/pixlr/web/controller/LearningController.java
===================================================================
diff -u -re3f085b7468e9e35eacf0d2f1dac9c7c6499c69a -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_pixlr/src/java/org/lamsfoundation/lams/tool/pixlr/web/controller/LearningController.java (.../LearningController.java) (revision e3f085b7468e9e35eacf0d2f1dac9c7c6499c69a)
+++ lams_tool_pixlr/src/java/org/lamsfoundation/lams/tool/pixlr/web/controller/LearningController.java (.../LearningController.java) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -154,7 +154,7 @@
// return to the viewAll images page if the user has already clicked it
if (pixlrUser.isFinishedActivity() && pixlr.isAllowViewOthersImages() && !isRedo) {
- return viewAllImages(request);
+ return viewAllImages(learningForm, request);
}
// set up the user dto
@@ -391,7 +391,8 @@
return finishActivity(learningForm, request, response);
}
- public String viewAllImages(HttpServletRequest request) {
+ @RequestMapping("/viewAllImages")
+ public String viewAllImages(@ModelAttribute("learningForm") LearningForm learningForm, HttpServletRequest request) {
Long toolSessionID = WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_SESSION_ID);
@@ -405,6 +406,7 @@
Pixlr pixlr = pixlrSession.getPixlr();
+ learningForm.setRedoQuestion(true);
// get the user
PixlrUser pixlrUser;
if (mode.equals(ToolAccessMode.TEACHER)) {
Index: lams_tool_pixlr/web/includes/javascript/authoring.js
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_pixlr/web/includes/javascript/authoring.js (.../authoring.js) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_pixlr/web/includes/javascript/authoring.js (.../authoring.js) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -36,6 +36,6 @@
selectTab(tabId);
}
function doSubmit(method) {
- document.authoringForm.dispatch.value = method;
- document.authoringForm.submit();
+ document.forms.authoringForm.action= method+".do";
+ document.forms.authoringForm.submit();
}
Index: lams_tool_pixlr/web/pages/learning/pixlr.jsp
===================================================================
diff -u -re508f067f2bb1975ccea77bda7a50f92c7b13b5d -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_pixlr/web/pages/learning/pixlr.jsp (.../pixlr.jsp) (revision e508f067f2bb1975ccea77bda7a50f92c7b13b5d)
+++ lams_tool_pixlr/web/pages/learning/pixlr.jsp (.../pixlr.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -33,7 +33,7 @@
}
function refresh() {
- window.location.href = "/learning.do?mode=${mode}&toolSessionID=${toolSessionID}&redoQuestion=true";
+ window.location.href = "learning.do?mode=${mode}&toolSessionID=${toolSessionID}&redoQuestion=true";
}
function disableFinishButton() {
@@ -133,20 +133,20 @@
-
-
+
-
+ !!
Index: lams_tool_pixlr/web/pages/learning/viewAll.jsp
===================================================================
diff -u -re3f085b7468e9e35eacf0d2f1dac9c7c6499c69a -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_pixlr/web/pages/learning/viewAll.jsp (.../viewAll.jsp) (revision e3f085b7468e9e35eacf0d2f1dac9c7c6499c69a)
+++ lams_tool_pixlr/web/pages/learning/viewAll.jsp (.../viewAll.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -37,7 +37,7 @@
-
+
Index: lams_tool_pixlr/web/pages/monitoring/summary.jsp
===================================================================
diff -u -r46e01bf046d755f571ba59957ac869c086bad58c -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_pixlr/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 46e01bf046d755f571ba59957ac869c086bad58c)
+++ lams_tool_pixlr/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -9,7 +9,7 @@
});
function submitForm(method, uid) {
- document.getElementById("dispatch").value = method;
+ document.getElementById("dispatch").action = method+".do";
document.getElementById("hideUserImageUid").value = uid;
document.getElementById("monitoringForm").submit();
}
Index: lams_tool_scratchie/.externalToolBuilders/SASS_lams_tool_scratchie.launch
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/.externalToolBuilders/SASS_lams_tool_scratchie.launch (.../SASS_lams_tool_scratchie.launch) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/.externalToolBuilders/SASS_lams_tool_scratchie.launch (.../SASS_lams_tool_scratchie.launch) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -5,6 +5,12 @@
+
+
+
+
+
+
Index: lams_tool_scratchie/build.xml
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/build.xml (.../build.xml) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/build.xml (.../build.xml) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -17,7 +17,7 @@
${ant.project.name}: Copying additional Java classes to WAR
Fisheye: Tag 22cca1a815f7aee530e14ab87b7744f381d03ac1 refers to a dead (removed) revision in file `lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/AdminAction.java'.
Fisheye: No comparison available. Pass `N' to diff?
Fisheye: Tag 22cca1a815f7aee530e14ab87b7744f381d03ac1 refers to a dead (removed) revision in file `lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/AuthoringAction.java'.
Fisheye: No comparison available. Pass `N' to diff?
Fisheye: Tag 22cca1a815f7aee530e14ab87b7744f381d03ac1 refers to a dead (removed) revision in file `lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/ClearSessionAction.java'.
Fisheye: No comparison available. Pass `N' to diff?
Fisheye: Tag 22cca1a815f7aee530e14ab87b7744f381d03ac1 refers to a dead (removed) revision in file `lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/LearningAction.java'.
Fisheye: No comparison available. Pass `N' to diff?
Fisheye: Tag 22cca1a815f7aee530e14ab87b7744f381d03ac1 refers to a dead (removed) revision in file `lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/LearningWebsocketServer.java'.
Fisheye: No comparison available. Pass `N' to diff?
Fisheye: Tag 22cca1a815f7aee530e14ab87b7744f381d03ac1 refers to a dead (removed) revision in file `lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/MonitoringAction.java'.
Fisheye: No comparison available. Pass `N' to diff?
Fisheye: Tag 22cca1a815f7aee530e14ab87b7744f381d03ac1 refers to a dead (removed) revision in file `lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/TblMonitorAction.java'.
Fisheye: No comparison available. Pass `N' to diff?
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/AdminController.java
===================================================================
diff -u
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/AdminController.java (revision 0)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/AdminController.java (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -0,0 +1,127 @@
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+package org.lamsfoundation.lams.tool.scratchie.web.controller;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang.StringUtils;
+import org.lamsfoundation.lams.tool.scratchie.ScratchieConstants;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieConfigItem;
+import org.lamsfoundation.lams.tool.scratchie.service.IScratchieService;
+import org.lamsfoundation.lams.tool.scratchie.web.form.AdminForm;
+import org.lamsfoundation.lams.util.MessageService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * @author Andrey Balan
+ */
+@Controller
+@RequestMapping("/admin")
+public class AdminController {
+
+ @Autowired
+ @Qualifier("scratchieService")
+ private IScratchieService scratchieService;
+
+ @Autowired
+ @Qualifier("scratchieMessageService")
+ private MessageService messageService;
+
+
+ @RequestMapping("/start")
+ public String start(@ModelAttribute("scratchieAdminForm") AdminForm scratchieAdminForm,
+ HttpServletRequest request) {
+
+ ScratchieConfigItem isEnabledExtraPointOption = scratchieService
+ .getConfigItem(ScratchieConfigItem.KEY_IS_ENABLED_EXTRA_POINT_OPTION);
+ if (isEnabledExtraPointOption != null) {
+ scratchieAdminForm.setEnabledExtraPointOption(new Boolean(isEnabledExtraPointOption.getConfigValue()));
+ }
+
+ ScratchieConfigItem presetMarks = scratchieService.getConfigItem(ScratchieConfigItem.KEY_PRESET_MARKS);
+ if (presetMarks != null) {
+ scratchieAdminForm.setPresetMarks(presetMarks.getConfigValue());
+ }
+
+ request.setAttribute("error", false);
+ return "pages/admin/config";
+ }
+
+ @RequestMapping(value = "/saveContent", method = RequestMethod.POST)
+ public String saveContent(@ModelAttribute("scratchieAdminForm") AdminForm scratchieAdminForm,
+ HttpServletRequest request) {
+
+ MultiValueMap errorMap = validateAdminForm(scratchieAdminForm);
+ if (!errorMap.isEmpty()) {
+ request.setAttribute("errorMap", errorMap);
+ return "pages/admin/config";
+ }
+
+ ScratchieConfigItem isEnabledExtraPointOption = scratchieService
+ .getConfigItem(ScratchieConfigItem.KEY_IS_ENABLED_EXTRA_POINT_OPTION);
+ isEnabledExtraPointOption.setConfigValue("" + scratchieAdminForm.isEnabledExtraPointOption());
+ scratchieService.saveOrUpdateScratchieConfigItem(isEnabledExtraPointOption);
+
+ ScratchieConfigItem presetMarks = scratchieService.getConfigItem(ScratchieConfigItem.KEY_PRESET_MARKS);
+ presetMarks.setConfigValue(scratchieAdminForm.getPresetMarks());
+ scratchieService.saveOrUpdateScratchieConfigItem(presetMarks);
+
+ request.setAttribute("savedSuccess", true);
+ return "pages/admin/config";
+
+ }
+
+ /**
+ * Validate ScratchieConfigItems.
+ *
+ * @param adminForm
+ * @return
+ */
+ private MultiValueMap validateAdminForm(
+ @ModelAttribute("scratchieAdminForm") AdminForm scratchieAdminForm) {
+ MultiValueMap errorMap = new LinkedMultiValueMap<>();
+
+ String presetMarks = scratchieAdminForm.getPresetMarks();
+ if (StringUtils.isNotBlank(presetMarks)) {
+
+ //it's not a comma separated numbers
+ if (!presetMarks.matches("[0-9]+(,[0-9]+)*")) {
+ errorMap.add("GLOBAL", messageService
+ .getMessage(ScratchieConstants.ERROR_MSG_ENTERED_MARKS_NOT_COMMA_SEPARATED_INTEGERS));
+ }
+
+ } else {
+ errorMap.add("GlOBAL", messageService.getMessage(ScratchieConstants.ERROR_MSG_REQUIRED_FIELDS_MISSING));
+ }
+
+ return errorMap;
+ }
+}
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/AuthoringController.java
===================================================================
diff -u
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/AuthoringController.java (revision 0)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/AuthoringController.java (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -0,0 +1,935 @@
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+package org.lamsfoundation.lams.tool.scratchie.web.controller;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.lang.math.NumberUtils;
+import org.apache.log4j.Logger;
+import org.lamsfoundation.lams.authoring.web.AuthoringConstants;
+import org.lamsfoundation.lams.learningdesign.ToolActivity;
+import org.lamsfoundation.lams.questions.Answer;
+import org.lamsfoundation.lams.questions.Question;
+import org.lamsfoundation.lams.questions.QuestionExporter;
+import org.lamsfoundation.lams.questions.QuestionParser;
+import org.lamsfoundation.lams.tool.ToolAccessMode;
+import org.lamsfoundation.lams.tool.scratchie.ScratchieConstants;
+import org.lamsfoundation.lams.tool.scratchie.model.Scratchie;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieAnswer;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieConfigItem;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieItem;
+import org.lamsfoundation.lams.tool.scratchie.service.IScratchieService;
+import org.lamsfoundation.lams.tool.scratchie.util.ScratchieAnswerComparator;
+import org.lamsfoundation.lams.tool.scratchie.util.ScratchieItemComparator;
+import org.lamsfoundation.lams.tool.scratchie.web.form.ScratchieForm;
+import org.lamsfoundation.lams.tool.scratchie.web.form.ScratchieItemForm;
+import org.lamsfoundation.lams.tool.scratchie.web.form.ScratchiePedagogicalPlannerForm;
+import org.lamsfoundation.lams.util.MessageService;
+import org.lamsfoundation.lams.util.WebUtil;
+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.util.MultiValueMap;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+/**
+ * @author Andrey Balan
+ */
+@Controller
+@RequestMapping("/authoring")
+public class AuthoringController {
+
+ private static Logger log = Logger.getLogger(AuthoringController.class);
+
+ @Autowired
+ @Qualifier("scratchieService")
+ private IScratchieService scratchieService;
+
+ @Autowired
+ @Qualifier("scratchieMessageService")
+ private MessageService messageService;
+
+ @RequestMapping("/start")
+ private String start(@ModelAttribute("authoringForm") ScratchieForm authoringForm, HttpServletRequest request)
+ throws ServletException {
+ ToolAccessMode mode = WebUtil.readToolAccessModeAuthorDefaulted(request);
+ request.setAttribute(AttributeNames.ATTR_MODE, mode.toString());
+ return starting(authoringForm, request);
+
+ }
+
+ @RequestMapping("/definelater")
+ private String definelater(@ModelAttribute("authoringForm") ScratchieForm authoringForm, HttpServletRequest request)
+ throws ServletException {
+ // update define later flag to true
+ Long contentId = new Long(WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_CONTENT_ID));
+ Scratchie scratchie = scratchieService.getScratchieByContentId(contentId);
+
+ scratchie.setDefineLater(true);
+ scratchieService.saveOrUpdateScratchie(scratchie);
+
+ //audit log the teacher has started editing activity in monitor
+ scratchieService.auditLogStartEditingActivityInMonitor(contentId);
+
+ request.setAttribute(AttributeNames.ATTR_MODE, ToolAccessMode.TEACHER.toString());
+ return starting(authoringForm, request);
+
+ }
+
+ private String starting(@ModelAttribute("authoringForm") ScratchieForm authoringForm, HttpServletRequest request)
+ throws ServletException {
+
+ // save toolContentID into HTTPSession
+ Long contentId = new Long(WebUtil.readLongParam(request, ScratchieConstants.PARAM_TOOL_CONTENT_ID));
+
+ List items = null;
+ Scratchie scratchie = null;
+
+ // initial Session Map
+ SessionMap sessionMap = new SessionMap<>();
+ request.getSession().setAttribute(sessionMap.getSessionID(), sessionMap);
+ authoringForm.setSessionMapID(sessionMap.getSessionID());
+
+ // Get contentFolderID and save to form.
+ String contentFolderID = WebUtil.readStrParam(request, AttributeNames.PARAM_CONTENT_FOLDER_ID);
+ sessionMap.put(AttributeNames.PARAM_CONTENT_FOLDER_ID, contentFolderID);
+ authoringForm.setContentFolderID(contentFolderID);
+
+ try {
+ scratchie = scratchieService.getScratchieByContentId(contentId);
+ // if scratchie does not exist, try to use default content instead.
+ if (scratchie == null) {
+ scratchie = scratchieService.getDefaultContent(contentId);
+ if (scratchie.getScratchieItems() != null) {
+ items = new ArrayList<>(scratchie.getScratchieItems());
+ } else {
+ items = null;
+ }
+ } else {
+ items = scratchieService.getAuthoredItems(scratchie.getUid());
+ }
+
+ authoringForm.setScratchie(scratchie);
+ } catch (Exception e) {
+ AuthoringController.log.error(e);
+ throw new ServletException(e);
+ }
+
+ ScratchieConfigItem isEnabledExtraPointOption = scratchieService
+ .getConfigItem(ScratchieConfigItem.KEY_IS_ENABLED_EXTRA_POINT_OPTION);
+ sessionMap.put(ScratchieConfigItem.KEY_IS_ENABLED_EXTRA_POINT_OPTION,
+ new Boolean(isEnabledExtraPointOption.getConfigValue()));
+
+ //prepare advanced option allowing to overwrite default preset marks
+ ScratchieConfigItem defaultPresetMarksConfigItem = scratchieService
+ .getConfigItem(ScratchieConfigItem.KEY_PRESET_MARKS);
+ String defaultPresetMarks = defaultPresetMarksConfigItem == null ? ""
+ : defaultPresetMarksConfigItem.getConfigValue();
+ boolean presetMarksOverwritten = scratchie.getPresetMarks() != null
+ && !scratchie.getPresetMarks().equals(defaultPresetMarks);
+ sessionMap.put(ScratchieConstants.ATTR_IS_PRESET_MARKS_OVERWRITTEN, presetMarksOverwritten);
+ sessionMap.put(ScratchieConstants.ATTR_DEFAULT_PRESET_MARKS, defaultPresetMarks);
+
+ // init it to avoid null exception in following handling
+ if (items == null) {
+ items = new ArrayList<>();
+ } else {
+ for (ScratchieItem item : items) {
+
+ // sort answers by order id. it's needed only for the default answers. rest could be skipped
+ TreeSet answerList = new TreeSet<>(new ScratchieAnswerComparator());
+ answerList.addAll(item.getAnswers());
+ item.setAnswers(answerList);
+ }
+ }
+ // init scratchie item list
+ SortedSet itemList = getItemList(sessionMap);
+ itemList.clear();
+ itemList.addAll(items);
+
+ // If there is no order id, set it up
+ int i = 1;
+ for (ScratchieItem scratchieItem : itemList) {
+ if (scratchieItem.getOrderId() == null || scratchieItem.getOrderId() != i) {
+ scratchieItem.setOrderId(i);
+ }
+ i++;
+ }
+
+ //display confidence providing activities
+ Set confidenceLevelsActivities = scratchieService
+ .getPrecedingConfidenceLevelsActivities(contentId);
+ sessionMap.put(ScratchieConstants.ATTR_CONFIDENCE_LEVELS_ACTIVITIES, confidenceLevelsActivities);
+
+ sessionMap.put(ScratchieConstants.ATTR_RESOURCE_FORM, authoringForm);
+ return "pages/authoring/start";
+ }
+
+ /**
+ * Display same entire authoring page content from HttpSession variable.
+ */
+ @RequestMapping("/initPage")
+ private String initPage(@ModelAttribute("authoringForm") ScratchieForm authoringForm, HttpServletRequest request)
+ throws ServletException {
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ ScratchieForm existForm = (ScratchieForm) sessionMap.get(ScratchieConstants.ATTR_RESOURCE_FORM);
+
+ try {
+ PropertyUtils.copyProperties(authoringForm, existForm);
+ } catch (Exception e) {
+ throw new ServletException(e);
+ }
+
+ ToolAccessMode mode = WebUtil.readToolAccessModeAuthorDefaulted(request);
+ request.setAttribute(AttributeNames.ATTR_MODE, mode.toString());
+
+ return "pages/authoring/start";
+ }
+
+ /**
+ * This method will persist all inforamtion in this authoring page, include all scratchie item, information etc.
+ */
+ @RequestMapping("/update")
+ private String updateContent(@ModelAttribute("authoringForm") ScratchieForm authoringForm,
+ HttpServletRequest request) throws Exception {
+
+ // get back sessionMAP
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(authoringForm.getSessionMapID());
+
+ ToolAccessMode mode = WebUtil.readToolAccessModeAuthorDefaulted(request);
+
+ Scratchie scratchie = authoringForm.getScratchie();
+
+ // **********************************Get Scratchie PO*********************
+ Scratchie scratchiePO = scratchieService.getScratchieByContentId(authoringForm.getScratchie().getContentId());
+
+ Set oldItems = null;
+
+ //allow using old and modified questions and references altogether
+ if (mode.isTeacher()) {
+ oldItems = (scratchiePO == null) ? new HashSet<>() : scratchiePO.getScratchieItems();
+
+ // initialize oldItems' answers
+ for (ScratchieItem oldItem : oldItems) {
+ for (ScratchieAnswer answer : (Set) oldItem.getAnswers()) {
+ }
+ }
+
+ scratchieService.releaseItemsFromCache(scratchiePO);
+ }
+
+ if (scratchiePO == null) {
+ // new Scratchie, create it.
+ scratchiePO = scratchie;
+ scratchiePO.setCreated(new Timestamp(new Date().getTime()));
+ scratchiePO.setUpdated(new Timestamp(new Date().getTime()));
+
+ } else {
+ Long uid = scratchiePO.getUid();
+ PropertyUtils.copyProperties(scratchiePO, scratchie);
+ // set back UID
+ scratchiePO.setUid(uid);
+
+ // if it is Teacher (from monitor) - change define later status
+ if (mode.isTeacher()) {
+ scratchiePO.setDefineLater(false);
+ }
+
+ scratchiePO.setUpdated(new Timestamp(new Date().getTime()));
+ }
+
+ // ************************* Handle scratchie items *******************
+ Set items = new LinkedHashSet<>();
+ SortedSet newItems = getItemList(sessionMap);
+ Iterator iter = newItems.iterator();
+ while (iter.hasNext()) {
+ ScratchieItem item = iter.next();
+ if (item != null) {
+ removeNewLineCharacters(item);
+ items.add(item);
+ }
+ }
+ scratchiePO.setScratchieItems(items);
+
+ //recalculate results in case content is edited from monitoring
+ List deletedItems = getDeletedItemList(sessionMap);
+ if (mode.isTeacher()) {
+ scratchieService.recalculateUserAnswers(scratchiePO, oldItems, newItems, deletedItems);
+ }
+
+ // delete items from database.
+ iter = deletedItems.iterator();
+ while (iter.hasNext()) {
+ ScratchieItem item = iter.next();
+ iter.remove();
+ if (item.getUid() != null) {
+ scratchieService.deleteScratchieItem(item.getUid());
+ }
+ }
+
+ // **********************************************
+ // finally persist scratchiePO again
+ scratchieService.saveOrUpdateScratchie(scratchiePO);
+
+ authoringForm.setScratchie(scratchiePO);
+
+ request.setAttribute(AuthoringConstants.LAMS_AUTHORING_SUCCESS_FLAG, Boolean.TRUE);
+ request.setAttribute(AttributeNames.ATTR_MODE, mode.toString());
+
+ return "pages/authoring/authoring";
+ }
+
+ /**
+ * Ajax call, will add one more input line for new resource item instruction.
+ */
+ @RequestMapping("/addItem")
+ private String addItem(@ModelAttribute("scratchieItemForm") ScratchieItemForm scratchieItemForm,
+ HttpServletRequest request) {
+
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ String contentFolderID = (String) sessionMap.get(AttributeNames.PARAM_CONTENT_FOLDER_ID);
+ scratchieItemForm.setSessionMapID(sessionMapID);
+ scratchieItemForm.setContentFolderID(contentFolderID);
+
+ List answerList = new ArrayList<>();
+ for (int i = 0; i < ScratchieConstants.INITIAL_ANSWERS_NUMBER; i++) {
+ ScratchieAnswer answer = new ScratchieAnswer();
+ answer.setOrderId(i + 1);
+ answerList.add(answer);
+ }
+ request.setAttribute(ScratchieConstants.ATTR_ANSWER_LIST, answerList);
+
+ request.setAttribute(AttributeNames.PARAM_CONTENT_FOLDER_ID, contentFolderID);
+ return "pages/authoring/parts/additem";
+ }
+
+ /**
+ * Display edit page for existed scratchie item.
+ */
+ @RequestMapping("/editItem")
+ private String editItem(@ModelAttribute("scratchieItemForm") ScratchieItemForm scratchieItemForm,
+ HttpServletRequest request) {
+
+ // get back sessionMAP
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ String contentFolderID = (String) sessionMap.get(AttributeNames.PARAM_CONTENT_FOLDER_ID);
+
+ int itemIdx = NumberUtils.toInt(request.getParameter(ScratchieConstants.PARAM_ITEM_INDEX), -1);
+ ScratchieItem item = null;
+ if (itemIdx != -1) {
+ SortedSet itemList = getItemList(sessionMap);
+ List rList = new ArrayList<>(itemList);
+ item = rList.get(itemIdx);
+ if (item != null) {
+ scratchieItemForm.setTitle(item.getTitle());
+ scratchieItemForm.setDescription(item.getDescription());
+ if (itemIdx >= 0) {
+ scratchieItemForm.setItemIndex(new Integer(itemIdx).toString());
+ }
+
+ Set answerList = item.getAnswers();
+ request.setAttribute(ScratchieConstants.ATTR_ANSWER_LIST, answerList);
+
+ scratchieItemForm.setContentFolderID(contentFolderID);
+ }
+ }
+ request.setAttribute(AttributeNames.PARAM_CONTENT_FOLDER_ID, contentFolderID);
+ return "pages/authoring/parts/additem";
+ }
+
+ /**
+ * This method will get necessary information from assessment question form and save or update into
+ * HttpSession
AssessmentQuestionList. Notice, this save is not persist them into database, just save
+ * HttpSession
temporarily. Only they will be persist when the entire authoring page is being
+ * persisted.
+ */
+
+ @RequestMapping(value = "/saveItem", method = RequestMethod.POST)
+ private String saveItem(@ModelAttribute("scratchieItemForm") ScratchieItemForm scratchieItemForm,
+ HttpServletRequest request) {
+
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(scratchieItemForm.getSessionMapID());
+ // check whether it is "edit(old Question)" or "add(new Question)"
+ SortedSet itemList = getItemList(sessionMap);
+ int itemIdx = NumberUtils.toInt(scratchieItemForm.getItemIndex(), -1);
+ ScratchieItem item = null;
+
+ if (itemIdx == -1) { // add
+ item = new ScratchieItem();
+ item.setCreateDate(new Timestamp(new Date().getTime()));
+ int maxSeq = 1;
+ if (itemList != null && itemList.size() > 0) {
+ ScratchieItem last = itemList.last();
+ maxSeq = last.getOrderId() + 1;
+ }
+ item.setOrderId(maxSeq);
+ itemList.add(item);
+ } else { // edit
+ List rList = new ArrayList<>(itemList);
+ item = rList.get(itemIdx);
+ }
+
+ item.setTitle(scratchieItemForm.getTitle());
+ item.setDescription(scratchieItemForm.getDescription());
+
+ // set options
+ Set answerList = getAnswersFromRequest(request, true);
+ Set answers = new LinkedHashSet<>();
+ int orderId = 0;
+ for (ScratchieAnswer answer : answerList) {
+ answer.setOrderId(orderId++);
+ answers.add(answer);
+ }
+ item.setAnswers(answers);
+
+ // set session map ID so that itemlist.jsp can get sessionMAP
+ request.setAttribute(ScratchieConstants.ATTR_SESSION_MAP_ID, scratchieItemForm.getSessionMapID());
+ return "pages/authoring/parts/itemlist";
+ }
+
+ /**
+ * Parses questions extracted from IMS QTI file and adds them as new items.
+ */
+ @SuppressWarnings("rawtypes")
+ @RequestMapping("/saveQTI")
+ private String saveQTI(HttpServletRequest request) throws UnsupportedEncodingException {
+ // big part of code was taken from saveItem() method
+ String sessionMapId = request.getParameter(ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapId);
+ String contentFolderID = (String) sessionMap.get(AttributeNames.PARAM_CONTENT_FOLDER_ID);
+ SortedSet itemList = getItemList(sessionMap);
+
+ Question[] questions = QuestionParser.parseQuestionChoiceForm(request);
+ for (Question question : questions) {
+ ScratchieItem item = new ScratchieItem();
+ item.setCreateDate(new Timestamp(new Date().getTime()));
+ int maxSeq = 1;
+ if (itemList != null && itemList.size() > 0) {
+ ScratchieItem last = itemList.last();
+ maxSeq = last.getOrderId() + 1;
+ }
+ item.setOrderId(maxSeq);
+ item.setTitle(question.getTitle());
+ item.setDescription(QuestionParser.processHTMLField(question.getText(), false, contentFolderID,
+ question.getResourcesFolderPath()));
+
+ TreeSet answerList = new TreeSet<>(new ScratchieAnswerComparator());
+ String correctAnswer = null;
+ int orderId = 1;
+ if (question.getAnswers() != null) {
+ for (Answer answer : question.getAnswers()) {
+ String answerText = QuestionParser.processHTMLField(answer.getText(), false, contentFolderID,
+ question.getResourcesFolderPath());
+ if (correctAnswer != null && correctAnswer.equals(answerText)) {
+ log.warn("Skipping an answer with same text as the correct answer: " + answerText);
+ continue;
+ }
+ ScratchieAnswer scratchieAnswer = new ScratchieAnswer();
+ scratchieAnswer.setDescription(answerText);
+ scratchieAnswer.setOrderId(orderId++);
+
+ if ((answer.getScore() != null) && (answer.getScore() > 0)) {
+ if (correctAnswer == null) {
+ scratchieAnswer.setCorrect(true);
+ correctAnswer = answerText;
+ } else {
+ log.warn(
+ "Choosing only first correct answer, despite another one was found: " + answerText);
+ scratchieAnswer.setCorrect(false);
+ }
+ } else {
+ scratchieAnswer.setCorrect(false);
+ }
+
+ answerList.add(scratchieAnswer);
+ }
+ }
+
+ if (correctAnswer == null) {
+ log.warn("No correct answer found for question: " + question.getText());
+ continue;
+ }
+
+ item.setAnswers(answerList);
+ itemList.add(item);
+ if (log.isDebugEnabled()) {
+ log.debug("Added question: " + question.getText());
+ }
+ }
+
+ request.setAttribute(ScratchieConstants.ATTR_SESSION_MAP_ID, sessionMapId);
+ return "pages/authoring/parts/itemlist";
+ }
+
+ /**
+ * Prepares Scratchie content for QTI packing
+ */
+ @SuppressWarnings({ "unchecked" })
+ @RequestMapping("/exportQTI")
+ private String exportQTI(HttpServletRequest request, HttpServletResponse response)
+ throws UnsupportedEncodingException {
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ SortedSet itemList = getItemList(sessionMap);
+ List questions = new LinkedList<>();
+
+ for (ScratchieItem item : itemList) {
+ Question question = new Question();
+
+ question.setType(Question.QUESTION_TYPE_MULTIPLE_CHOICE);
+ question.setTitle(item.getTitle());
+ question.setText(item.getDescription());
+
+ List answers = new ArrayList<>();
+ Set scratchieAnswers = new TreeSet<>(new ScratchieAnswerComparator());
+ scratchieAnswers.addAll(item.getAnswers());
+
+ for (ScratchieAnswer itemAnswer : scratchieAnswers) {
+ Answer answer = new Answer();
+ answer.setText(itemAnswer.getDescription());
+ // there is no LAMS interface to adjust, so use the default 1 point
+ Float score = itemAnswer.isCorrect() ? 1F : 0;
+ answer.setScore(score);
+ answers.add(answer);
+ }
+
+ question.setAnswers(answers);
+ questions.add(question);
+ }
+
+ String title = request.getParameter("title");
+ QuestionExporter exporter = new QuestionExporter(title, questions.toArray(Question.QUESTION_ARRAY_TYPE));
+ exporter.exportQTIPackage(request, response);
+
+ return null;
+ }
+
+ /**
+ * Ajax call, remove the given line of instruction of resource item.
+ *
+ * @param mapping
+ * @param form
+ * @param request
+ * @param response
+ * @return
+ */
+ @RequestMapping("/removeItem")
+ private String removeItem(HttpServletRequest request) {
+
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ request.setAttribute(ScratchieConstants.ATTR_SESSION_MAP_ID, sessionMapID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ SortedSet itemList = getItemList(sessionMap);
+
+ int itemIndex = NumberUtils.toInt(request.getParameter(ScratchieConstants.PARAM_ITEM_INDEX), -1);
+ if (itemIndex != -1) {
+ List rList = new ArrayList<>(itemList);
+ ScratchieItem item = rList.remove(itemIndex);
+ itemList.clear();
+ itemList.addAll(rList);
+
+ // add to delList
+ List delList = getDeletedItemList(sessionMap);
+ delList.add(item);
+ }
+
+ request.setAttribute(ScratchieConstants.ATTR_ITEM_LIST, itemList);
+ return "pages/authoring/parts/itemlist";
+ }
+
+ /**
+ * Move up current item.
+ *
+ * @param mapping
+ * @param form
+ * @param request
+ * @param response
+ * @return
+ */
+ @RequestMapping("/upItem")
+ private String upItem(HttpServletRequest request) {
+ return switchItem(request, true);
+ }
+
+ /**
+ * Move down current item.
+ *
+ * @param mapping
+ * @param form
+ * @param request
+ * @param response
+ * @return
+ */
+ @RequestMapping("/downItem")
+ private String downItem(HttpServletRequest request) {
+ return switchItem(request, false);
+ }
+
+ private String switchItem(HttpServletRequest request, boolean up) {
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ request.setAttribute(ScratchieConstants.ATTR_SESSION_MAP_ID, sessionMapID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ SortedSet itemList = getItemList(sessionMap);
+
+ int itemIndex = NumberUtils.toInt(request.getParameter(ScratchieConstants.PARAM_ITEM_INDEX), -1);
+ if (itemIndex != -1) {
+ List rList = new ArrayList<>(itemList);
+
+ // get current and the target item, and switch their sequnece
+ ScratchieItem item = rList.get(itemIndex);
+ ScratchieItem repOption;
+ if (up) {
+ repOption = rList.get(--itemIndex);
+ } else {
+ repOption = rList.get(++itemIndex);
+ }
+
+ int upSeqId = repOption.getOrderId();
+ repOption.setOrderId(item.getOrderId());
+ item.setOrderId(upSeqId);
+
+ // put back list, it will be sorted again
+ itemList.clear();
+ itemList.addAll(rList);
+ }
+
+ request.setAttribute(ScratchieConstants.ATTR_ITEM_LIST, itemList);
+ return "pages/authoring/parts/itemlist";
+ }
+
+ // ----------------------- Answers functions ---------------
+
+ /**
+ * Ajax call, will add one more input line for new resource item instruction.
+ *
+ * @param mapping
+ * @param form
+ * @param request
+ * @param response
+ * @return
+ */
+ @RequestMapping("/addAnswer")
+ private String addAnswer(HttpServletRequest request) {
+
+ SortedSet answerList = getAnswersFromRequest(request, false);
+
+ ScratchieAnswer answer = new ScratchieAnswer();
+ int maxSeq = 1;
+ if (answerList != null && answerList.size() > 0) {
+ ScratchieAnswer last = answerList.last();
+ maxSeq = last.getOrderId() + 1;
+ }
+ answer.setOrderId(maxSeq);
+ answerList.add(answer);
+
+ request.setAttribute(ScratchieConstants.ATTR_ANSWER_LIST, answerList);
+ request.setAttribute(AttributeNames.PARAM_CONTENT_FOLDER_ID,
+ WebUtil.readStrParam(request, AttributeNames.PARAM_CONTENT_FOLDER_ID));
+ return "pages/authoring/parts/answerlist";
+ }
+
+ /**
+ * Ajax call, remove the given answer.
+ *
+ * @param mapping
+ * @param form
+ * @param request
+ * @param response
+ * @return
+ */
+ @RequestMapping("/removeAnswer")
+ private String removeAnswer(HttpServletRequest request) {
+
+ SortedSet answerList = getAnswersFromRequest(request, false);
+
+ int answerIndex = NumberUtils.toInt(request.getParameter(ScratchieConstants.PARAM_ANSWER_INDEX), -1);
+ if (answerIndex != -1) {
+ List rList = new ArrayList<>(answerList);
+ rList.remove(answerIndex);
+ answerList.clear();
+ answerList.addAll(rList);
+ }
+
+ request.setAttribute(ScratchieConstants.ATTR_ANSWER_LIST, answerList);
+ request.setAttribute(AttributeNames.PARAM_CONTENT_FOLDER_ID,
+ WebUtil.readStrParam(request, AttributeNames.PARAM_CONTENT_FOLDER_ID));
+ return "pages/authoring/parts/answerlist";
+ }
+
+ /**
+ * Move up current answer.
+ *
+ * @param mapping
+ * @param form
+ * @param request
+ * @param response
+ * @return
+ */
+ @RequestMapping("/upAnswer")
+ private String upAnswer(HttpServletRequest request) {
+ return switchAnswer(request, true);
+ }
+
+ /**
+ * Move down current answer.
+ *
+ * @param mapping
+ * @param form
+ * @param request
+ * @param response
+ * @return
+ */
+ @RequestMapping("/downAnswer")
+ private String downAnswer(HttpServletRequest request) {
+ return switchAnswer(request, false);
+ }
+
+ private String switchAnswer(HttpServletRequest request, boolean up) {
+ SortedSet answerList = getAnswersFromRequest(request, false);
+
+ int answerIndex = NumberUtils.toInt(request.getParameter(ScratchieConstants.PARAM_ANSWER_INDEX), -1);
+ if (answerIndex != -1) {
+ List rList = new ArrayList<>(answerList);
+
+ // get current and the target item, and switch their sequnece
+ ScratchieAnswer answer = rList.get(answerIndex);
+ ScratchieAnswer repAnswer;
+ if (up) {
+ repAnswer = rList.get(--answerIndex);
+ } else {
+ repAnswer = rList.get(++answerIndex);
+ }
+
+ int upSeqId = repAnswer.getOrderId();
+ repAnswer.setOrderId(answer.getOrderId());
+ answer.setOrderId(upSeqId);
+
+ // put back list, it will be sorted again
+ answerList.clear();
+ answerList.addAll(rList);
+ }
+
+ request.setAttribute(ScratchieConstants.ATTR_ANSWER_LIST, answerList);
+ return "pages/authoring/parts/answerlist";
+ }
+
+ // ----------------------- PedagogicalPlannerForm ---------------
+
+ @RequestMapping("/initPedagogicalPlannerForm")
+ public String initPedagogicalPlannerForm(
+ @ModelAttribute("pedagogicalPlannerForm") ScratchiePedagogicalPlannerForm pedagogicalPlannerForm,
+ HttpServletRequest request) {
+ Long toolContentID = WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_CONTENT_ID);
+ Scratchie scratchie = scratchieService.getScratchieByContentId(toolContentID);
+ pedagogicalPlannerForm.fillForm(scratchie);
+ String contentFolderId = WebUtil.readStrParam(request, AttributeNames.PARAM_CONTENT_FOLDER_ID);
+ pedagogicalPlannerForm.setContentFolderID(contentFolderId);
+ return "pages/authoring/pedagogicalPlannerForm";
+ }
+
+ @RequestMapping(value = "/saveOrUpdatePedagogicalPlannerForm", method = RequestMethod.POST)
+ public String saveOrUpdatePedagogicalPlannerForm(
+ @ModelAttribute("pedagogicalPlannerForm") ScratchiePedagogicalPlannerForm pedagogicalPlannerForm,
+ HttpServletRequest request) throws IOException {
+ MultiValueMap errorMap = pedagogicalPlannerForm.validate(messageService);
+ if (errorMap.isEmpty()) {
+ Scratchie scratchie = scratchieService.getScratchieByContentId(pedagogicalPlannerForm.getToolContentID());
+ scratchie.setInstructions(pedagogicalPlannerForm.getInstructions());
+ scratchieService.saveOrUpdateScratchie(scratchie);
+ } else {
+ request.setAttribute("errorMap", errorMap);
+ }
+ return "pages/authoring/pedagogicalPlannerForm";
+ }
+
+ // *************************************************************************************
+ // Private method
+ // *************************************************************************************
+ /**
+ * List save current scratchie items.
+ *
+ * @param request
+ * @return
+ */
+ private SortedSet getItemList(SessionMap sessionMap) {
+ SortedSet list = (SortedSet) sessionMap.get(ScratchieConstants.ATTR_ITEM_LIST);
+ if (list == null) {
+ list = new TreeSet<>(new ScratchieItemComparator());
+ sessionMap.put(ScratchieConstants.ATTR_ITEM_LIST, list);
+ }
+ return list;
+ }
+
+ /**
+ * List save deleted scratchie items, which could be persisted or non-persisted items.
+ *
+ * @param request
+ * @return
+ */
+ private List getDeletedItemList(SessionMap sessionMap) {
+ return getListFromSession(sessionMap, ScratchieConstants.ATTR_DELETED_ITEM_LIST);
+ }
+
+ /**
+ * Get java.util.List
from HttpSession by given name.
+ *
+ * @param request
+ * @param name
+ * @return
+ */
+ private List getListFromSession(SessionMap sessionMap, String name) {
+ List list = (List) sessionMap.get(name);
+ if (list == null) {
+ list = new ArrayList();
+ sessionMap.put(name, list);
+ }
+ return list;
+ }
+
+ /**
+ * Get answer options from HttpRequest
+ *
+ * @param request
+ * @param isForSaving
+ * whether the blank options will be preserved or not
+ *
+ */
+ private TreeSet getAnswersFromRequest(HttpServletRequest request, boolean isForSaving) {
+ Map paramMap = splitRequestParameter(request, ScratchieConstants.ATTR_ANSWER_LIST);
+ Integer correctAnswerIndex = (paramMap.get(ScratchieConstants.ATTR_ANSWER_CORRECT) == null) ? null
+ : NumberUtils.toInt(paramMap.get(ScratchieConstants.ATTR_ANSWER_CORRECT));
+
+ int count = NumberUtils.toInt(paramMap.get(ScratchieConstants.ATTR_ANSWER_COUNT));
+ TreeSet answerList = new TreeSet<>(new ScratchieAnswerComparator());
+ for (int i = 0; i < count; i++) {
+
+ String answerDescription = paramMap.get(ScratchieConstants.ATTR_ANSWER_DESCRIPTION_PREFIX + i);
+ if ((answerDescription == null) && isForSaving) {
+ continue;
+ }
+
+ ScratchieAnswer answer = new ScratchieAnswer();
+ String uidStr = paramMap.get(ScratchieConstants.ATTR_ANSWER_UID_PREFIX + i);
+ if (uidStr != null) {
+ Long uid = NumberUtils.toLong(uidStr);
+ answer.setUid(uid);
+ }
+ String orderIdStr = paramMap.get(ScratchieConstants.ATTR_ANSWER_ORDER_ID_PREFIX + i);
+ Integer orderId = NumberUtils.toInt(orderIdStr);
+ answer.setOrderId(orderId);
+ answer.setDescription(answerDescription);
+ if ((correctAnswerIndex != null) && correctAnswerIndex.equals(orderId)) {
+ answer.setCorrect(true);
+ }
+ answerList.add(answer);
+ }
+
+ return answerList;
+ }
+
+ /**
+ * Split Request Parameter from HttpRequest
+ *
+ * @param request
+ * @param parameterName
+ * parameterName
+ */
+ private Map splitRequestParameter(HttpServletRequest request, String parameterName) {
+ String list = request.getParameter(parameterName);
+ if (list == null) {
+ return null;
+ }
+
+ String[] params = list.split("&");
+ Map paramMap = new HashMap<>();
+ String[] pair;
+ for (String item : params) {
+ pair = item.split("=");
+ if (pair == null || pair.length != 2) {
+ continue;
+ }
+ try {
+ paramMap.put(pair[0], URLDecoder.decode(pair[1], "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ log.error("Error occurs when decode instruction string:" + e.toString());
+ }
+ }
+ return paramMap;
+ }
+
+ /**
+ * Removes redundant new line characters from options left by CKEditor (otherwise it will break Javascript in
+ * monitor)
+ */
+ private void removeNewLineCharacters(ScratchieItem item) {
+ Set answers = item.getAnswers();
+ if (answers != null) {
+ for (ScratchieAnswer answer : answers) {
+ String answerDescription = answer.getDescription();
+ if (answerDescription != null) {
+ answerDescription = answerDescription.replaceAll("[\n\r\f]", "");
+ answer.setDescription(answerDescription);
+ }
+ }
+
+ }
+ }
+
+}
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/ClearSessionController.java
===================================================================
diff -u
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/ClearSessionController.java (revision 0)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/ClearSessionController.java (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -0,0 +1,62 @@
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+package org.lamsfoundation.lams.tool.scratchie.web.controller;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.lamsfoundation.lams.authoring.web.LamsAuthoringFinishController;
+import org.lamsfoundation.lams.tool.ToolAccessMode;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * This class give a chance to clear HttpSession when user save/close authoring page.
+ *
+ * @author @author Andrey Balan
+ */
+@Controller
+public class ClearSessionController extends LamsAuthoringFinishController {
+
+ @Autowired
+ private WebApplicationContext applicationContext;
+
+ @RequestMapping(value = "/clearsession")
+ public void execute(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ super.execute(request, response, applicationContext);
+ }
+
+ @Override
+ public void clearSession(String customiseSessionID, HttpSession session, ToolAccessMode mode) {
+ if (mode.isAuthor()) {
+ session.removeAttribute(customiseSessionID);
+ }
+ }
+
+}
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/LearningController.java
===================================================================
diff -u
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/LearningController.java (revision 0)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/LearningController.java (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -0,0 +1,764 @@
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+package org.lamsfoundation.lams.tool.scratchie.web.controller;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.TimeZone;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.NumberUtils;
+import org.apache.log4j.Logger;
+import org.lamsfoundation.lams.learning.web.bean.ActivityPositionDTO;
+import org.lamsfoundation.lams.learning.web.util.LearningWebUtil;
+import org.lamsfoundation.lams.notebook.model.NotebookEntry;
+import org.lamsfoundation.lams.notebook.service.CoreNotebookConstants;
+import org.lamsfoundation.lams.tool.ToolAccessMode;
+import org.lamsfoundation.lams.tool.scratchie.ScratchieConstants;
+import org.lamsfoundation.lams.tool.scratchie.dto.BurningQuestionItemDTO;
+import org.lamsfoundation.lams.tool.scratchie.dto.ReflectDTO;
+import org.lamsfoundation.lams.tool.scratchie.model.Scratchie;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieAnswer;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieBurningQuestion;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieItem;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieSession;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieUser;
+import org.lamsfoundation.lams.tool.scratchie.service.IScratchieService;
+import org.lamsfoundation.lams.tool.scratchie.service.ScratchieApplicationException;
+import org.lamsfoundation.lams.tool.scratchie.web.form.ReflectionForm;
+import org.lamsfoundation.lams.usermanagement.User;
+import org.lamsfoundation.lams.usermanagement.dto.UserDTO;
+import org.lamsfoundation.lams.util.DateUtil;
+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.quartz.SchedulerException;
+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.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * @author Andrey Balan
+ */
+@Controller
+@RequestMapping("/learning")
+public class LearningController {
+
+ private static Logger log = Logger.getLogger(LearningController.class);
+
+ @Autowired
+ @Qualifier("scratchieService")
+ private IScratchieService scratchieService;
+
+ @Autowired
+ private WebApplicationContext applicationContext;
+
+ /**
+ * Read scratchie data from database and put them into HttpSession.
+ */
+ @RequestMapping("/start")
+ private String start(HttpServletRequest request, HttpServletResponse response)
+ throws ScratchieApplicationException {
+
+ ToolAccessMode mode = WebUtil.readToolAccessModeParam(request, AttributeNames.PARAM_MODE, true);
+ final Long toolSessionId = new Long(request.getParameter(ScratchieConstants.PARAM_TOOL_SESSION_ID));
+ ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(toolSessionId);
+ // get back the scratchie and item list and display them on page
+ final Scratchie scratchie = scratchieService.getScratchieBySessionId(toolSessionId);
+ boolean isReflectOnActivity = scratchie.isReflectOnActivity();
+
+ final ScratchieUser user;
+ if ((mode != null) && mode.isTeacher()) {
+ // monitoring mode - user is specified in URL
+ // scratchieUser may be null if the user was force completed.
+ user = getSpecifiedUser(toolSessionId, WebUtil.readIntParam(request, AttributeNames.PARAM_USER_ID, false));
+ } else {
+ user = getCurrentUser(toolSessionId);
+ }
+
+ ScratchieUser groupLeader = scratchieService.checkLeaderSelectToolForSessionLeader(user, toolSessionId);
+
+ // forwards to the leaderSelection page
+ if (groupLeader == null) {
+
+ // get group users and store it to request as DTO objects
+ List groupUsers = scratchieService.getUsersBySession(toolSessionId);
+ List groupUserDtos = new ArrayList<>();
+ for (ScratchieUser groupUser : groupUsers) {
+ User groupUserDto = new User();
+ groupUserDto.setFirstName(groupUser.getFirstName());
+ groupUserDto.setLastName(groupUser.getLastName());
+ groupUserDtos.add(groupUserDto);
+ }
+ request.setAttribute(ScratchieConstants.ATTR_GROUP_USERS, groupUserDtos);
+ request.setAttribute(ScratchieConstants.PARAM_TOOL_SESSION_ID, toolSessionId);
+ request.setAttribute(ScratchieConstants.ATTR_SCRATCHIE, scratchie);
+ request.setAttribute(AttributeNames.ATTR_MODE, mode);
+ return "pages/learning/waitforleader";
+ }
+
+ // initial Session Map
+ SessionMap sessionMap = new SessionMap<>();
+ request.getSession().setAttribute(sessionMap.getSessionID(), sessionMap);
+ request.setAttribute(ScratchieConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID());
+
+ // get notebook entry
+ NotebookEntry notebookEntry = null;
+ if (isReflectOnActivity && (groupLeader != null)) {
+ notebookEntry = scratchieService.getEntry(toolSessionId, CoreNotebookConstants.NOTEBOOK_TOOL,
+ ScratchieConstants.TOOL_SIGNATURE, groupLeader.getUserId().intValue());
+ }
+ String entryText = (notebookEntry == null) ? null : notebookEntry.getEntry();
+
+ // basic information
+ sessionMap.put(ScratchieConstants.ATTR_TITLE, scratchie.getTitle());
+ sessionMap.put(ScratchieConstants.ATTR_RESOURCE_INSTRUCTION, scratchie.getInstructions());
+ sessionMap.put(ScratchieConstants.ATTR_USER_ID, user.getUserId());
+ sessionMap.put(ScratchieConstants.ATTR_USER_UID, user.getUid());
+ String groupLeaderName = groupLeader.getFirstName() + " " + groupLeader.getLastName();
+ sessionMap.put(ScratchieConstants.ATTR_GROUP_LEADER_NAME, groupLeaderName);
+ sessionMap.put(ScratchieConstants.ATTR_GROUP_LEADER_USER_ID, groupLeader.getUserId());
+ boolean isUserLeader = toolSession.isUserGroupLeader(user.getUid());
+ sessionMap.put(ScratchieConstants.ATTR_IS_USER_LEADER, isUserLeader);
+ boolean isUserFinished = (user != null) && user.isSessionFinished();
+ sessionMap.put(ScratchieConstants.ATTR_USER_FINISHED, isUserFinished);
+ sessionMap.put(AttributeNames.PARAM_TOOL_SESSION_ID, toolSessionId);
+ sessionMap.put(AttributeNames.ATTR_MODE, mode);
+ sessionMap.put(ScratchieConstants.ATTR_IS_BURNING_QUESTIONS_ENABLED, scratchie.isBurningQuestionsEnabled());
+ // reflection information
+ sessionMap.put(ScratchieConstants.ATTR_REFLECTION_ON, isReflectOnActivity);
+ sessionMap.put(ScratchieConstants.ATTR_REFLECTION_INSTRUCTION, scratchie.getReflectInstructions());
+ sessionMap.put(ScratchieConstants.ATTR_REFLECTION_ENTRY, entryText);
+ // add all answer uids to one set
+ if (isUserLeader) {
+ Set answerUids = new HashSet<>();
+ for (ScratchieItem item : scratchie.getScratchieItems()) {
+ for (ScratchieAnswer answer : (Set) item.getAnswers()) {
+ answerUids.add(answer.getUid());
+ }
+ }
+ sessionMap.put(ScratchieConstants.ATTR_ANSWER_UIDS, answerUids);
+ }
+
+ // add define later support
+ if (scratchie.isDefineLater()) {
+ return "pages/learning/definelater";
+ }
+
+ ActivityPositionDTO activityPosition = LearningWebUtil.putActivityPositionInRequestByToolSessionId(
+ toolSessionId, request, applicationContext.getServletContext());
+ sessionMap.put(AttributeNames.ATTR_ACTIVITY_POSITION, activityPosition);
+
+ // check if there is submission deadline
+ Date submissionDeadline = scratchie.getSubmissionDeadline();
+ if (submissionDeadline != null) {
+ // store submission deadline to sessionMap
+ sessionMap.put(ScratchieConstants.ATTR_SUBMISSION_DEADLINE, submissionDeadline);
+
+ HttpSession ss = SessionManager.getSession();
+ UserDTO learnerDto = (UserDTO) ss.getAttribute(AttributeNames.USER);
+ TimeZone learnerTimeZone = learnerDto.getTimeZone();
+ Date tzSubmissionDeadline = DateUtil.convertToTimeZoneFromDefault(learnerTimeZone, submissionDeadline);
+ Date currentLearnerDate = DateUtil.convertToTimeZoneFromDefault(learnerTimeZone, new Date());
+
+ // calculate whether submission deadline has passed, and if so forward to "submissionDeadline"
+ if (currentLearnerDate.after(tzSubmissionDeadline)) {
+ return "pages/learning/submissionDeadline";
+ }
+ }
+
+ storeItemsToSessionMap(toolSessionId, scratchie, sessionMap, mode.isTeacher());
+
+ sessionMap.put(ScratchieConstants.ATTR_SCRATCHIE, scratchie);
+ // calculate max score
+ int maxScore = scratchieService.getMaxPossibleScore(scratchie);
+ sessionMap.put(ScratchieConstants.ATTR_MAX_SCORE, maxScore);
+
+ boolean isScratchingFinished = toolSession.isScratchingFinished();
+ boolean isWaitingForLeaderToSubmitNotebook = isReflectOnActivity && (notebookEntry == null);
+ boolean isShowResults = (isScratchingFinished && !isWaitingForLeaderToSubmitNotebook) && !mode.isTeacher();
+
+ // show notebook page to the leader
+ if (isUserLeader && isScratchingFinished && isWaitingForLeaderToSubmitNotebook) {
+
+ String redirectURL = "redirect:/learning/newReflection.do";
+ redirectURL = WebUtil.appendParameterToURL(redirectURL, ScratchieConstants.ATTR_SESSION_MAP_ID,
+ sessionMap.getSessionID());
+ redirectURL = WebUtil.appendParameterToURL(redirectURL, AttributeNames.ATTR_MODE, mode.toString());
+ return redirectURL;
+
+ // show results page
+ } else if (isShowResults) {
+
+ String redirectURL = "redirect:learning/showResults.do";
+ redirectURL = WebUtil.appendParameterToURL(redirectURL, ScratchieConstants.ATTR_SESSION_MAP_ID,
+ sessionMap.getSessionID());
+ redirectURL = WebUtil.appendParameterToURL(redirectURL, AttributeNames.ATTR_MODE, mode.toString());
+ return redirectURL;
+
+ // show learning.jsp page
+ } else {
+
+ // time limit feature
+ boolean isTimeLimitEnabled = isUserLeader && !isScratchingFinished && scratchie.getTimeLimit() != 0;
+ boolean isTimeLimitNotLaunched = toolSession.getTimeLimitLaunchedDate() == null;
+ long secondsLeft = 1;
+ if (isTimeLimitEnabled) {
+ // if user has pressed OK button already - calculate remaining time, and full time otherwise
+ secondsLeft = isTimeLimitNotLaunched ? scratchie.getTimeLimit() * 60
+ : scratchie.getTimeLimit() * 60
+ - (System.currentTimeMillis() - toolSession.getTimeLimitLaunchedDate().getTime())
+ / 1000;
+ // change negative number or zero to 1 so it can autosubmit results
+ secondsLeft = Math.max(1, secondsLeft);
+ }
+ request.setAttribute(ScratchieConstants.ATTR_IS_TIME_LIMIT_ENABLED, isTimeLimitEnabled);
+ request.setAttribute(ScratchieConstants.ATTR_IS_TIME_LIMIT_NOT_LAUNCHED, isTimeLimitNotLaunched);
+ request.setAttribute(ScratchieConstants.ATTR_SECONDS_LEFT, secondsLeft);
+
+ // in case we can't show learning.jsp to non-leaders forward them to the waitForLeaderTimeLimit page
+ if (!isUserLeader && scratchie.getTimeLimit() != 0 && !mode.isTeacher()) {
+
+ // show waitForLeaderLaunchTimeLimit page if the leader hasn't started activity or hasn't pressed OK
+ // button to launch time limit
+ if (toolSession.getTimeLimitLaunchedDate() == null) {
+ request.setAttribute(ScratchieConstants.ATTR_WAITING_MESSAGE_KEY,
+ "label.waiting.for.leader.launch.time.limit");
+ return "pages/learning/waitForLeaderTimeLimit";
+ }
+
+ // check if the time limit is exceeded
+ boolean isTimeLimitExceeded = toolSession.getTimeLimitLaunchedDate().getTime()
+ + scratchie.getTimeLimit() * 60000 < System.currentTimeMillis();
+
+ // if the time limit is over and the leader hasn't submitted notebook or burning questions (thus
+ // non-leaders should wait) - show waitForLeaderFinish page
+ if (isTimeLimitExceeded && isWaitingForLeaderToSubmitNotebook) {
+ request.setAttribute(ScratchieConstants.ATTR_WAITING_MESSAGE_KEY,
+ "label.waiting.for.leader.submit.notebook");
+ return "pages/learning/waitForLeaderTimeLimit";
+ }
+ }
+
+ sessionMap.put(ScratchieConstants.ATTR_IS_SCRATCHING_FINISHED, isScratchingFinished);
+ // make non-leaders wait for notebook to be submitted, if required
+ sessionMap.put(ScratchieConstants.ATTR_IS_WAITING_FOR_LEADER_TO_SUBMIT_NOTEBOOK,
+ isWaitingForLeaderToSubmitNotebook);
+ return "pages/learning/learning";
+ }
+
+ }
+
+ /**
+ * Stores into session map all data needed to display scratchies and answers
+ */
+ private void storeItemsToSessionMap(Long toolSessionId, Scratchie scratchie, SessionMap sessionMap,
+ boolean showOrder) {
+ // set scratched flag for display purpose
+ Collection items = scratchieService.getItemsWithIndicatedScratches(toolSessionId);
+
+ // shuffling items
+ if (scratchie.isShuffleItems()) {
+ //items is a Set at this moment
+ ArrayList shuffledItems = new ArrayList<>(items);
+ //use random with a seed so people from the same group get the "same shuffle"
+ Random randomGenerator = new Random(toolSessionId);
+ Collections.shuffle(shuffledItems, randomGenerator);
+ items = shuffledItems;
+ }
+
+ // for teacher in monitoring display the number of attempt.
+ if (showOrder) {
+ scratchieService.getScratchesOrder(items, toolSessionId);
+ }
+
+ //display confidence levels
+ if (scratchie.isConfidenceLevelsEnabled()) {
+ scratchieService.populateItemsWithConfidenceLevels((Long) sessionMap.get(ScratchieConstants.ATTR_USER_ID),
+ toolSessionId, scratchie.getConfidenceLevelsActivityUiid(), items);
+ }
+
+ // populate items with the existing burning questions for displaying purposes
+ List burningQuestions = null;
+ if (scratchie.isBurningQuestionsEnabled()) {
+
+ burningQuestions = scratchieService.getBurningQuestionsBySession(toolSessionId);
+ for (ScratchieItem item : items) {
+
+ // find corresponding burningQuestion
+ String question = "";
+ for (ScratchieBurningQuestion burningQuestion : burningQuestions) {
+ if (!burningQuestion.isGeneralQuestion()
+ && burningQuestion.getScratchieItem().getUid().equals(item.getUid())) {
+ question = burningQuestion.getQuestion();
+ break;
+ }
+ }
+ item.setBurningQuestion(question);
+ }
+
+ // find general burning question
+ String generalBurningQuestion = "";
+ for (ScratchieBurningQuestion burningQuestion : burningQuestions) {
+ if (burningQuestion.isGeneralQuestion()) {
+ generalBurningQuestion = burningQuestion.getQuestion();
+ break;
+ }
+ }
+ sessionMap.put(ScratchieConstants.ATTR_GENERAL_BURNING_QUESTION, generalBurningQuestion);
+ }
+
+ sessionMap.put(ScratchieConstants.ATTR_ITEM_LIST, items);
+ }
+
+ /**
+ * Record in DB that leader has scratched specified answer. And return whether scratchie answer is correct or not.
+ */
+ @RequestMapping("/recordItemScratched")
+ private String recordItemScratched(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ScratchieApplicationException {
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ final Long toolSessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID);
+ final Long answerUid = NumberUtils.createLong(request.getParameter(ScratchieConstants.PARAM_ANSWER_UID));
+
+ ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(toolSessionId);
+
+ ScratchieUser leader = getCurrentUser(toolSessionId);
+ // only leader is allowed to scratch answers
+ if (!toolSession.isUserGroupLeader(leader.getUid())) {
+ return null;
+ }
+
+ // check answer is belong to current session
+ Set answerUids = (Set) sessionMap.get(ScratchieConstants.ATTR_ANSWER_UIDS);
+ if (!answerUids.contains(answerUid)) {
+ return null;
+ }
+
+ // Return whether scratchie answer is correct or not
+ ScratchieAnswer answer = scratchieService.getScratchieAnswerByUid(answerUid);
+ if (answer == null) {
+ return null;
+ }
+
+ ObjectNode ObjectNode = JsonNodeFactory.instance.objectNode();
+ ObjectNode.put(ScratchieConstants.ATTR_ANSWER_CORRECT, answer.isCorrect());
+ response.setContentType("application/x-json;charset=utf-8");
+ response.getWriter().print(ObjectNode);
+
+ // create a new thread to record item scratched (in order to do this task in parallel not to slow down sending
+ // response back)
+ Thread recordItemScratchedThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ scratchieService.recordItemScratched(toolSessionId, answerUid);
+ }
+ }, "LAMS_recordItemScratched_thread");
+ recordItemScratchedThread.start();
+
+ return null;
+ }
+
+ /**
+ * Stores date when user has started activity with time limit.
+ */
+ @RequestMapping("/launchTimeLimit")
+ private String launchTimeLimit(HttpServletRequest request)
+ throws ScratchieApplicationException, SchedulerException {
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ final Long toolSessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID);
+ ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(toolSessionId);
+
+ ScratchieUser leader = getCurrentUser(toolSessionId);
+ // only leader is allowed to launch time limit
+ if (!toolSession.isUserGroupLeader(leader.getUid())) {
+ return null;
+ }
+
+ scratchieService.launchTimeLimit(toolSessionId);
+
+ return null;
+ }
+
+ /**
+ * Displays results page. When leader gets to this page, scratchingFinished column is set to true for all users.
+ */
+ @RequestMapping("/showResults")
+ private String showResults(HttpServletRequest request) throws ScratchieApplicationException, IOException {
+ // get back SessionMap
+ String sessionMapID = request.getParameter(ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ request.setAttribute(ScratchieConstants.ATTR_SESSION_MAP_ID, sessionMapID);
+ boolean isReflectOnActivity = (Boolean) sessionMap.get(ScratchieConstants.ATTR_REFLECTION_ON);
+ boolean isBurningQuestionsEnabled = (Boolean) sessionMap
+ .get(ScratchieConstants.ATTR_IS_BURNING_QUESTIONS_ENABLED);
+
+ final Long toolSessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID);
+ ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(toolSessionId);
+ Scratchie scratchie = toolSession.getScratchie();
+ Long userUid = (Long) sessionMap.get(ScratchieConstants.ATTR_USER_UID);
+
+ //handle burning questions saving if needed
+ if (toolSession.isUserGroupLeader(userUid) && scratchie.isBurningQuestionsEnabled()
+ && !toolSession.isScratchingFinished()) {
+ saveBurningQuestions(request);
+ }
+
+ if (toolSession.isUserGroupLeader(userUid) && !toolSession.isScratchingFinished()) {
+ scratchieService.setScratchingFinished(toolSessionId);
+ }
+
+ // get updated score from ScratchieSession
+ int score = toolSession.getMark();
+ int maxScore = (Integer) sessionMap.get(ScratchieConstants.ATTR_MAX_SCORE);
+ double percentage = (maxScore == 0) ? 0 : ((score * 100) / maxScore);
+ request.setAttribute(ScratchieConstants.ATTR_SCORE, (int) percentage);
+
+ // display other groups' BurningQuestions
+ if (isBurningQuestionsEnabled) {
+ List burningQuestionItemDtos = scratchieService.getBurningQuestionDtos(scratchie,
+ toolSessionId, false);
+ request.setAttribute(ScratchieConstants.ATTR_BURNING_QUESTION_ITEM_DTOS, burningQuestionItemDtos);
+ }
+
+ // display other groups' notebooks
+ if (isReflectOnActivity) {
+ List reflections = scratchieService
+ .getReflectionList(toolSession.getScratchie().getContentId());
+
+ // remove current session leader reflection
+ Iterator refIterator = reflections.iterator();
+ while (refIterator.hasNext()) {
+ ReflectDTO reflection = refIterator.next();
+ if (toolSession.getSessionName().equals(reflection.getGroupName())) {
+
+ // store for displaying purposes
+ String reflectEntry = StringEscapeUtils.unescapeJavaScript(reflection.getReflection());
+ sessionMap.put(ScratchieConstants.ATTR_REFLECTION_ENTRY, reflectEntry);
+
+ // remove from list to display other groups' notebooks
+ refIterator.remove();
+ break;
+ }
+ }
+
+ request.setAttribute(ScratchieConstants.ATTR_REFLECTIONS, reflections);
+ }
+
+ if (scratchie.isShowScrachiesInResults()) {
+ storeItemsToSessionMap(toolSessionId, scratchie, sessionMap, true);
+ request.setAttribute(ScratchieConstants.ATTR_SHOW_RESULTS, true);
+ }
+
+ return "pages/learning/results";
+ }
+
+ /**
+ * Saves newly entered burning question. Used by jqGrid cellediting feature.
+ */
+ @RequestMapping("/editBurningQuestion")
+ private String editBurningQuestion(HttpServletRequest request) {
+
+ if (!StringUtils.isEmpty(request.getParameter(ScratchieConstants.ATTR_ITEM_UID))
+ && !StringUtils.isEmpty(request.getParameter(ScratchieConstants.PARAM_SESSION_ID))) {
+
+ Long itemUid = WebUtil.readLongParam(request, ScratchieConstants.ATTR_ITEM_UID) == 0 ? null
+ : WebUtil.readLongParam(request, ScratchieConstants.ATTR_ITEM_UID);
+ Long sessionId = WebUtil.readLongParam(request, ScratchieConstants.PARAM_SESSION_ID);
+ String question = request.getParameter(ScratchieConstants.ATTR_BURNING_QUESTION_PREFIX);
+ scratchieService.saveBurningQuestion(sessionId, itemUid, question);
+ }
+
+ return null;
+ }
+
+ @RequestMapping("/like")
+ private String like(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException, ScratchieApplicationException {
+
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ final Long sessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID);
+ ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(sessionId);
+
+ Long burningQuestionUid = WebUtil.readLongParam(request, ScratchieConstants.PARAM_BURNING_QUESTION_UID);
+
+ ScratchieUser leader = this.getCurrentUser(sessionId);
+ // only leader is allowed to scratch answers
+ if (!toolSession.isUserGroupLeader(leader.getUid())) {
+ return null;
+ }
+
+ boolean added = scratchieService.addLike(burningQuestionUid, sessionId);
+
+ ObjectNode ObjectNode = JsonNodeFactory.instance.objectNode();
+ ObjectNode.put("added", added);
+ response.setContentType("application/json;charset=utf-8");
+ response.getWriter().print(ObjectNode);
+ return null;
+ }
+
+ @RequestMapping("/removeLike")
+ private String removeLike(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException, ScratchieApplicationException {
+
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ final Long sessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID);
+ ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(sessionId);
+
+ Long burningQuestionUid = WebUtil.readLongParam(request, ScratchieConstants.PARAM_BURNING_QUESTION_UID);
+
+ ScratchieUser leader = this.getCurrentUser(sessionId);
+ // only leader is allowed to scratch answers
+ if (!toolSession.isUserGroupLeader(leader.getUid())) {
+ return null;
+ }
+
+ scratchieService.removeLike(burningQuestionUid, sessionId);
+
+ ObjectNode ObjectNode = JsonNodeFactory.instance.objectNode();
+ ObjectNode.put("added", true);
+ response.setContentType("application/json;charset=utf-8");
+ response.getWriter().print(ObjectNode);
+ return null;
+ }
+
+ /**
+ * Finish learning session.
+ */
+ @RequestMapping("/finish")
+ private String finish(HttpServletRequest request) {
+ // get back SessionMap
+ String sessionMapID = request.getParameter(ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ final Long toolSessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID);
+ HttpSession ss = SessionManager.getSession();
+ UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER);
+ final Long userId = user.getUserID().longValue();
+
+ String nextActivityUrl = null;
+
+ try {
+ nextActivityUrl = scratchieService.finishToolSession(toolSessionId, userId);
+
+ request.setAttribute(ScratchieConstants.ATTR_NEXT_ACTIVITY_URL, nextActivityUrl);
+ } catch (ScratchieApplicationException e) {
+ log.error("Failed get next activity url:" + e.getMessage());
+ }
+ return "pages/learning/finish";
+ }
+
+ /**
+ * Autosaves burning questions. Only leaders can perform it.
+ */
+ @RequestMapping("/autosaveBurningQuestions")
+ private String autosaveBurningQuestions(HttpServletRequest request) throws ScratchieApplicationException {
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ final Long sessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID);
+
+ // only leader is allowed to submit burning questions
+ ScratchieUser leader = getCurrentUser(sessionId);
+ ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(sessionId);
+ if (!toolSession.isUserGroupLeader(leader.getUid())) {
+ return null;
+ }
+
+ saveBurningQuestions(request);
+
+ return null;
+ }
+
+ /**
+ * Saves burning questions entered by user. It also updates its values in SessionMap.
+ */
+
+ private void saveBurningQuestions(HttpServletRequest request) {
+
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ final Long sessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID);
+ Collection items = (Collection) sessionMap.get(ScratchieConstants.ATTR_ITEM_LIST);
+
+ for (ScratchieItem item : items) {
+ final Long itemUid = item.getUid();
+
+ final String burningQuestion = request
+ .getParameter(ScratchieConstants.ATTR_BURNING_QUESTION_PREFIX + itemUid);
+ // update question in sessionMap
+ item.setBurningQuestion(burningQuestion);
+
+ // update new entry
+ scratchieService.saveBurningQuestion(sessionId, itemUid, burningQuestion);
+ }
+
+ // handle general burning question
+ final String generalQuestion = request.getParameter(ScratchieConstants.ATTR_GENERAL_BURNING_QUESTION);
+ scratchieService.saveBurningQuestion(sessionId, null, generalQuestion);
+ // update general question in sessionMap
+ sessionMap.put(ScratchieConstants.ATTR_GENERAL_BURNING_QUESTION, generalQuestion);
+ }
+
+ /**
+ * Display empty reflection form.
+ */
+ @RequestMapping("/newReflection")
+ private String newReflection(@ModelAttribute("reflectionForm") ReflectionForm reflectionForm,
+ HttpServletRequest request) throws ScratchieApplicationException, IOException {
+
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ final Long toolSessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID);
+ Long userUid = (Long) sessionMap.get(ScratchieConstants.ATTR_USER_UID);
+
+ HttpSession ss = SessionManager.getSession();
+ UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER);
+
+ reflectionForm.setUserID(user.getUserID());
+ reflectionForm.setSessionMapID(sessionMapID);
+
+ // get the existing reflection entry
+ NotebookEntry entry = scratchieService.getEntry(toolSessionId, CoreNotebookConstants.NOTEBOOK_TOOL,
+ ScratchieConstants.TOOL_SIGNATURE, user.getUserID());
+
+ if (entry != null) {
+ reflectionForm.setEntryText(entry.getEntry());
+ }
+
+ ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(toolSessionId);
+ Scratchie scratchie = toolSession.getScratchie();
+
+ //handle burning questions saving if needed
+ if (toolSession.isUserGroupLeader(userUid) && scratchie.isBurningQuestionsEnabled()
+ && !toolSession.isScratchingFinished()) {
+ saveBurningQuestions(request);
+ }
+
+ // in case of the leader we should let all other learners see Next Activity button
+ if (toolSession.isUserGroupLeader(userUid) && !toolSession.isScratchingFinished()) {
+ scratchieService.setScratchingFinished(toolSessionId);
+ }
+
+ return "pages/learning/notebook";
+ }
+
+ /**
+ * Submit reflection form input database. Only leaders can submit reflections.
+ */
+ @RequestMapping("/submitReflection")
+ private String submitReflection(@ModelAttribute("reflectionForm") ReflectionForm reflectionForm,
+ HttpServletRequest request) throws ScratchieApplicationException {
+ final Integer userId = reflectionForm.getUserID();
+ final String entryText = reflectionForm.getEntryText();
+
+ String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ final Long sessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID);
+
+ // check for existing notebook entry
+ final NotebookEntry entry = scratchieService.getEntry(sessionId, CoreNotebookConstants.NOTEBOOK_TOOL,
+ ScratchieConstants.TOOL_SIGNATURE, userId);
+
+ if (entry == null) {
+ // create new entry
+ scratchieService.createNotebookEntry(sessionId, CoreNotebookConstants.NOTEBOOK_TOOL,
+ ScratchieConstants.TOOL_SIGNATURE, userId, entryText);
+ } else {
+ // update existing entry
+ entry.setEntry(entryText);
+ entry.setLastModified(new Date());
+ scratchieService.updateEntry(entry);
+ }
+ sessionMap.put(ScratchieConstants.ATTR_REFLECTION_ENTRY, entryText);
+
+ String redirectURL = "redirect:/learning/showResults.do";
+ redirectURL = WebUtil.appendParameterToURL(redirectURL, ScratchieConstants.ATTR_SESSION_MAP_ID,
+ sessionMap.getSessionID());
+ return redirectURL;
+ }
+
+ // *************************************************************************************
+ // Private method
+ // *************************************************************************************
+
+ private ScratchieUser getCurrentUser(Long sessionId) throws ScratchieApplicationException {
+ // try to get form system session
+ HttpSession ss = SessionManager.getSession();
+ // get back login user DTO
+ UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER);
+ ScratchieUser scratchieUser = scratchieService.getUserByIDAndSession(user.getUserID().longValue(), sessionId);
+
+ if (scratchieUser == null) {
+ ScratchieSession session = scratchieService.getScratchieSessionBySessionId(sessionId);
+ final ScratchieUser newScratchieUser = new ScratchieUser(user, session);
+ scratchieService.createUser(newScratchieUser);
+
+ scratchieUser = newScratchieUser;
+ }
+ return scratchieUser;
+ }
+
+ private ScratchieUser getSpecifiedUser(Long sessionId, Integer userId) {
+ ScratchieUser scratchieUser = scratchieService.getUserByIDAndSession(userId.longValue(), sessionId);
+ if (scratchieUser == null) {
+ log.error("Unable to find specified user for scratchie activity. Screens are likely to fail. SessionId="
+ + sessionId + " UserId=" + userId);
+ }
+ return scratchieUser;
+ }
+}
\ No newline at end of file
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/LearningWebsocketServer.java
===================================================================
diff -u
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/LearningWebsocketServer.java (revision 0)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/LearningWebsocketServer.java (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -0,0 +1,303 @@
+package org.lamsfoundation.lams.tool.scratchie.web.controller;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.websocket.CloseReason;
+import javax.websocket.CloseReason.CloseCodes;
+import javax.websocket.OnClose;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+import org.apache.log4j.Logger;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieAnswer;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieItem;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieSession;
+import org.lamsfoundation.lams.tool.scratchie.service.IScratchieService;
+import org.lamsfoundation.lams.tool.scratchie.service.ScratchieServiceProxy;
+import org.lamsfoundation.lams.util.hibernate.HibernateSessionManager;
+import org.lamsfoundation.lams.web.session.SessionManager;
+import org.lamsfoundation.lams.web.util.AttributeNames;
+
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Sends Scratchies actions to non-leaders.
+ *
+ * @author Marcin Cieslak
+ */
+@ServerEndpoint("/learningWebsocket")
+public class LearningWebsocketServer {
+
+ /**
+ * A singleton which updates Learners with reports and votes.
+ */
+ private static class SendWorker extends Thread {
+ private boolean stopFlag = false;
+ // how ofter the thread runs
+ private static final long CHECK_INTERVAL = 3000;
+
+ @Override
+ public void run() {
+ while (!stopFlag) {
+ try {
+ // websocket communication bypasses standard HTTP filters, so Hibernate session needs to be initialised manually
+ HibernateSessionManager.openSession();
+ Iterator>> entryIterator = LearningWebsocketServer.websockets.entrySet()
+ .iterator();
+ // go through activities and update registered learners with reports and vote count
+ while (entryIterator.hasNext()) {
+ Entry> entry = entryIterator.next();
+ Long toolSessionId = entry.getKey();
+ // if all learners left the activity, remove the obsolete mapping
+ Set sessionWebsockets = entry.getValue();
+ if (sessionWebsockets.isEmpty()) {
+ entryIterator.remove();
+ LearningWebsocketServer.cache.remove(toolSessionId);
+ continue;
+ }
+
+ ScratchieSession toolSession = LearningWebsocketServer.getScratchieService()
+ .getScratchieSessionBySessionId(toolSessionId);
+ boolean timeLimitUp = false;
+ boolean scratchingFinished = toolSession.isScratchingFinished();
+ // is Scratchie time limited?
+ if (toolSession.getTimeLimitLaunchedDate() != null) {
+ // missing whole cache is a marker that we already checked time limit and it is up
+ if (LearningWebsocketServer.cache.get(toolSessionId) == null) {
+ timeLimitUp = true;
+ } else {
+ // calculate whether time limit is up
+ Calendar currentTime = new GregorianCalendar(TimeZone.getDefault());
+ Calendar timeLimitFinishDate = new GregorianCalendar(TimeZone.getDefault());
+ timeLimitFinishDate.setTime(toolSession.getTimeLimitLaunchedDate());
+ timeLimitFinishDate.add(Calendar.MINUTE, toolSession.getScratchie().getTimeLimit());
+ //adding 5 extra seconds to let leader auto-submit results and store them in DB
+ timeLimitFinishDate.add(Calendar.SECOND, 5);
+ if (timeLimitFinishDate.compareTo(currentTime) <= 0) {
+ // time is up
+ if (!scratchingFinished) {
+ // Leader did not finish scratching yet? Pity. We do it for him
+ LearningWebsocketServer.getScratchieService()
+ .setScratchingFinished(toolSessionId);
+ scratchingFinished = true;
+ }
+ // mark time limit as up with this trick
+ LearningWebsocketServer.cache.remove(toolSessionId);
+ timeLimitUp = true;
+ }
+ }
+ }
+
+ boolean isWaitingForLeaderToSubmit = LearningWebsocketServer.getScratchieService()
+ .isWaitingForLeaderToSubmitNotebook(toolSession);
+ if (timeLimitUp) {
+ // time limit is up
+ if (isWaitingForLeaderToSubmit) {
+ // if Leader did not finish burning questions or notebook, push non-leaders to wait page
+ LearningWebsocketServer.sendPageRefreshRequest(toolSessionId);
+ } else {
+ // if Leader finished everything, non-leaders see Finish button
+ LearningWebsocketServer.sendCloseRequest(toolSessionId);
+ }
+ } else if (scratchingFinished && !isWaitingForLeaderToSubmit) {
+ // time limit not set or not up yet, but everything is finished
+ // show non-leaders Finish button
+ LearningWebsocketServer.sendCloseRequest(toolSessionId);
+ } else {
+ // regular send of scratched items
+ SendWorker.send(toolSessionId);
+ }
+ }
+ } catch (Exception e) {
+ // error caught, but carry on
+ LearningWebsocketServer.log.error("Error in Scratchie worker thread", e);
+ } finally {
+ HibernateSessionManager.closeSession();
+ try {
+ Thread.sleep(SendWorker.CHECK_INTERVAL);
+ } catch (InterruptedException e) {
+ LearningWebsocketServer.log.warn("Stopping Scratchie worker thread");
+ stopFlag = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Feeds websockets with scratched answers.
+ */
+ @SuppressWarnings("unchecked")
+ private static void send(Long toolSessionId) throws IOException {
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+
+ Collection items = LearningWebsocketServer.getScratchieService()
+ .getItemsWithIndicatedScratches(toolSessionId);
+ Map> sessionCache = LearningWebsocketServer.cache.get(toolSessionId);
+ for (ScratchieItem item : items) {
+ Long itemUid = item.getUid();
+ // do not init variables below until it's really needed
+ Map itemCache = null;
+ ObjectNode itemJSON = null;
+ for (ScratchieAnswer answer : (Set) item.getAnswers()) {
+ if (answer.isScratched()) {
+ // answer is scratched, check if it is present in cache
+ if (itemCache == null) {
+ itemCache = sessionCache.get(itemUid);
+ if (itemCache == null) {
+ itemCache = new TreeMap<>();
+ sessionCache.put(itemUid, itemCache);
+ }
+ }
+
+ Long answerUid = answer.getUid();
+ Boolean answerStoredIsCorrect = answer.isCorrect();
+ Boolean answerCache = itemCache.get(answerUid);
+ // check if the correct answer is stored in cache
+ if ((answerCache == null) || !answerCache.equals(answerStoredIsCorrect)) {
+ // send only updates, nothing Learners are already aware of
+ itemCache.put(answerUid, answerStoredIsCorrect);
+ if (itemJSON == null) {
+ itemJSON = JsonNodeFactory.instance.objectNode();
+ }
+ itemJSON.put(answerUid.toString(), answerStoredIsCorrect);
+ }
+ }
+ }
+ if (itemJSON != null) {
+ responseJSON.set(itemUid.toString(), itemJSON);
+ }
+ }
+
+ // are there any updates to send?
+ if (responseJSON.size() == 0) {
+ return;
+ }
+
+ String response = responseJSON.toString();
+ Set sessionWebsockets = LearningWebsocketServer.websockets.get(toolSessionId);
+ for (Session websocket : sessionWebsockets) {
+ websocket.getBasicRemote().sendText(response);
+ }
+ }
+ }
+
+ private static final Logger log = Logger.getLogger(LearningWebsocketServer.class);
+
+ private static IScratchieService scratchieService;
+
+ private static final SendWorker sendWorker = new SendWorker();
+ // maps toolSessionId -> itemUid -> answerUid -> isCorrect
+ private static final Map>> cache = new ConcurrentHashMap<>();
+ private static final Map> websockets = new ConcurrentHashMap<>();
+
+ static {
+ // run the singleton thread
+ LearningWebsocketServer.sendWorker.start();
+ }
+
+ /**
+ * Registeres the Learner for processing by SendWorker.
+ */
+ @OnOpen
+ public void registerUser(Session websocket) throws IOException {
+ Long toolSessionId = Long
+ .valueOf(websocket.getRequestParameterMap().get(AttributeNames.PARAM_TOOL_SESSION_ID).get(0));
+ Set sessionWebsockets = LearningWebsocketServer.websockets.get(toolSessionId);
+ if (sessionWebsockets == null) {
+ sessionWebsockets = ConcurrentHashMap.newKeySet();
+ LearningWebsocketServer.websockets.put(toolSessionId, sessionWebsockets);
+
+ Map> sessionCache = new TreeMap<>();
+ LearningWebsocketServer.cache.put(toolSessionId, sessionCache);
+ }
+ sessionWebsockets.add(websocket);
+
+ if (LearningWebsocketServer.log.isDebugEnabled()) {
+ LearningWebsocketServer.log.debug("User " + websocket.getUserPrincipal().getName()
+ + " entered Scratchie with toolSessionId: " + toolSessionId);
+ }
+ }
+
+ /**
+ * When user leaves the activity.
+ */
+ @OnClose
+ public void unregisterUser(Session websocket, CloseReason reason) {
+ Long toolSessionId = Long
+ .valueOf(websocket.getRequestParameterMap().get(AttributeNames.PARAM_TOOL_SESSION_ID).get(0));
+ LearningWebsocketServer.websockets.get(toolSessionId).remove(websocket);
+
+ if (LearningWebsocketServer.log.isDebugEnabled()) {
+ // If there was something wrong with the connection, put it into logs.
+ LearningWebsocketServer.log.debug("User " + websocket.getUserPrincipal().getName()
+ + " left Scratchie with Tool Session ID: " + toolSessionId
+ + (!(reason.getCloseCode().equals(CloseCodes.GOING_AWAY)
+ || reason.getCloseCode().equals(CloseCodes.NORMAL_CLOSURE))
+ ? ". Abnormal close. Code: " + reason.getCloseCode() + ". Reason: "
+ + reason.getReasonPhrase()
+ : ""));
+ }
+ }
+
+ /**
+ * The leader finished scratching and also . Non-leaders will have
+ * Finish button displayed.
+ */
+ public static void sendCloseRequest(Long toolSessionId) throws IOException {
+ Set sessionWebsockets = LearningWebsocketServer.websockets.get(toolSessionId);
+ if (sessionWebsockets == null) {
+ return;
+ }
+
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ responseJSON.put("close", true);
+ String response = responseJSON.toString();
+
+ for (Session websocket : sessionWebsockets) {
+ if (websocket.isOpen()) {
+ websocket.getBasicRemote().sendText(response);
+ }
+ }
+ }
+
+ /**
+ * The time limit is expired but leader hasn't submitted required notebook/burning questions yet. Non-leaders
+ * will need to refresh the page in order to stop showing them questions page.
+ */
+ public static void sendPageRefreshRequest(Long toolSessionId) throws IOException {
+ Set sessionWebsockets = LearningWebsocketServer.websockets.get(toolSessionId);
+ if (sessionWebsockets == null) {
+ return;
+ }
+
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ responseJSON.put("pageRefresh", true);
+ String response = responseJSON.toString();
+
+ for (Session websocket : sessionWebsockets) {
+ if (websocket.isOpen()) {
+ websocket.getBasicRemote().sendText(response);
+ }
+ }
+ }
+
+ private static IScratchieService getScratchieService() {
+ if (LearningWebsocketServer.scratchieService == null) {
+ LearningWebsocketServer.scratchieService = ScratchieServiceProxy
+ .getScratchieService(SessionManager.getServletContext());
+ }
+ return LearningWebsocketServer.scratchieService;
+ }
+}
\ No newline at end of file
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/MonitoringController.java
===================================================================
diff -u
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/MonitoringController.java (revision 0)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/MonitoringController.java (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -0,0 +1,296 @@
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+package org.lamsfoundation.lams.tool.scratchie.web.controller;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.TimeZone;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.lamsfoundation.lams.tool.scratchie.ScratchieConstants;
+import org.lamsfoundation.lams.tool.scratchie.dto.BurningQuestionItemDTO;
+import org.lamsfoundation.lams.tool.scratchie.dto.GroupSummary;
+import org.lamsfoundation.lams.tool.scratchie.dto.LeaderResultsDTO;
+import org.lamsfoundation.lams.tool.scratchie.dto.ReflectDTO;
+import org.lamsfoundation.lams.tool.scratchie.model.Scratchie;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieAnswer;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieItem;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieUser;
+import org.lamsfoundation.lams.tool.scratchie.service.IScratchieService;
+import org.lamsfoundation.lams.usermanagement.dto.UserDTO;
+import org.lamsfoundation.lams.util.DateUtil;
+import org.lamsfoundation.lams.util.ExcelCell;
+import org.lamsfoundation.lams.util.ExcelUtil;
+import org.lamsfoundation.lams.util.FileUtil;
+import org.lamsfoundation.lams.util.JsonUtil;
+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 com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+@Controller
+@RequestMapping("/monitoring")
+public class MonitoringController {
+ public static Logger log = Logger.getLogger(MonitoringController.class);
+
+ @Autowired
+ @Qualifier("scratchieService")
+ private IScratchieService scratchieService;
+
+ @RequestMapping("/summary")
+ private String summary(HttpServletRequest request) {
+
+ // initialize Session Map
+ SessionMap sessionMap = new SessionMap<>();
+ request.getSession().setAttribute(sessionMap.getSessionID(), sessionMap);
+ request.setAttribute(ScratchieConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID());
+
+ Long contentId = WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_CONTENT_ID);
+ List summaryList = scratchieService.getMonitoringSummary(contentId, true);
+
+ Scratchie scratchie = scratchieService.getScratchieByContentId(contentId);
+ Set learners = scratchieService.getAllLeaders(contentId);
+
+ //set SubmissionDeadline, if any
+ if (scratchie.getSubmissionDeadline() != null) {
+ Date submissionDeadline = scratchie.getSubmissionDeadline();
+ HttpSession ss = SessionManager.getSession();
+ UserDTO teacher = (UserDTO) ss.getAttribute(AttributeNames.USER);
+ TimeZone teacherTimeZone = teacher.getTimeZone();
+ Date tzSubmissionDeadline = DateUtil.convertToTimeZoneFromDefault(teacherTimeZone, submissionDeadline);
+ request.setAttribute(ScratchieConstants.ATTR_SUBMISSION_DEADLINE, tzSubmissionDeadline.getTime());
+ request.setAttribute(ScratchieConstants.ATTR_SUBMISSION_DEADLINE_DATESTRING,
+ DateUtil.convertToStringForJSON(submissionDeadline, request.getLocale()));
+ }
+
+ // cache into sessionMap
+ boolean isGroupedActivity = scratchieService.isGroupedActivity(contentId);
+ sessionMap.put(ScratchieConstants.ATTR_IS_GROUPED_ACTIVITY, isGroupedActivity);
+ sessionMap.put(ScratchieConstants.ATTR_SUMMARY_LIST, summaryList);
+ sessionMap.put(ScratchieConstants.ATTR_SCRATCHIE, scratchie);
+ sessionMap.put(ScratchieConstants.ATTR_LEARNERS, learners);
+ sessionMap.put(ScratchieConstants.ATTR_TOOL_CONTENT_ID, contentId);
+ sessionMap.put(AttributeNames.PARAM_CONTENT_FOLDER_ID,
+ WebUtil.readStrParam(request, AttributeNames.PARAM_CONTENT_FOLDER_ID));
+ sessionMap.put(ScratchieConstants.ATTR_REFLECTION_ON, scratchie.isReflectOnActivity());
+
+ // Create BurningQuestionsDtos if BurningQuestions is enabled.
+ if (scratchie.isBurningQuestionsEnabled()) {
+ List burningQuestionItemDtos = scratchieService.getBurningQuestionDtos(scratchie,
+ null, true);
+ sessionMap.put(ScratchieConstants.ATTR_BURNING_QUESTION_ITEM_DTOS, burningQuestionItemDtos);
+ }
+
+ // Create reflectList if reflection is enabled.
+ if (scratchie.isReflectOnActivity()) {
+ List reflections = scratchieService.getReflectionList(contentId);
+ sessionMap.put(ScratchieConstants.ATTR_REFLECTIONS, reflections);
+ }
+
+ return "pages/monitoring/monitoring";
+ }
+
+ @RequestMapping("/itemSummary")
+ private String itemSummary(HttpServletRequest request) {
+
+ String sessionMapID = request.getParameter(ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ request.setAttribute(ScratchieConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID());
+
+ Long itemUid = WebUtil.readLongParam(request, ScratchieConstants.ATTR_ITEM_UID);
+ if (itemUid.equals(-1)) {
+ return null;
+ }
+ ScratchieItem item = scratchieService.getScratchieItemByUid(itemUid);
+ request.setAttribute(ScratchieConstants.ATTR_ITEM, item);
+
+ Long contentId = (Long) sessionMap.get(ScratchieConstants.ATTR_TOOL_CONTENT_ID);
+ List summaryList = scratchieService.getQuestionSummary(contentId, itemUid);
+
+ // escape JS sensitive characters in answer descriptions
+ for (GroupSummary summary : summaryList) {
+ for (ScratchieAnswer answer : summary.getAnswers()) {
+ String description = (answer.getDescription() == null) ? ""
+ : StringEscapeUtils.escapeJavaScript(answer.getDescription());
+ answer.setDescription(description);
+ }
+ }
+
+ request.setAttribute(ScratchieConstants.ATTR_SUMMARY_LIST, summaryList);
+ return "pages/monitoring/parts/itemSummary";
+ }
+
+ @RequestMapping("/saveUserMark")
+ private String saveUserMark(HttpServletRequest request) {
+
+ if ((request.getParameter(ScratchieConstants.PARAM_NOT_A_NUMBER) == null)
+ && !StringUtils.isEmpty(request.getParameter(ScratchieConstants.ATTR_USER_ID))
+ && !StringUtils.isEmpty(request.getParameter(ScratchieConstants.PARAM_SESSION_ID))) {
+
+ Long userId = WebUtil.readLongParam(request, ScratchieConstants.ATTR_USER_ID);
+ Long sessionId = WebUtil.readLongParam(request, ScratchieConstants.PARAM_SESSION_ID);
+ Integer newMark = Integer.valueOf(request.getParameter(ScratchieConstants.PARAM_MARK));
+ scratchieService.changeUserMark(userId, sessionId, newMark);
+ }
+
+ return null;
+ }
+
+ /**
+ * Set Submission Deadline
+ *
+ * @param mapping
+ * @param form
+ * @param request
+ * @param response
+ * @return
+ * @throws IOException
+ */
+ @RequestMapping("/setSubmissionDeadline")
+ private String setSubmissionDeadline(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+ Long contentID = WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_CONTENT_ID);
+ Scratchie scratchie = scratchieService.getScratchieByContentId(contentID);
+
+ Long dateParameter = WebUtil.readLongParam(request, ScratchieConstants.ATTR_SUBMISSION_DEADLINE, true);
+ Date tzSubmissionDeadline = null;
+ String formattedDate = "";
+ if (dateParameter != null) {
+ Date submissionDeadline = new Date(dateParameter);
+ HttpSession ss = SessionManager.getSession();
+ UserDTO teacher = (UserDTO) ss.getAttribute(AttributeNames.USER);
+ TimeZone teacherTimeZone = teacher.getTimeZone();
+ tzSubmissionDeadline = DateUtil.convertFromTimeZoneToDefault(teacherTimeZone, submissionDeadline);
+ formattedDate = DateUtil.convertToStringForJSON(tzSubmissionDeadline, request.getLocale());
+ }
+ scratchie.setSubmissionDeadline(tzSubmissionDeadline);
+ scratchieService.saveOrUpdateScratchie(scratchie);
+
+ response.setContentType("text/plain;charset=utf-8");
+ response.getWriter().print(formattedDate);
+ return null;
+ }
+
+ /**
+ * Exports tool results into excel.
+ *
+ * @throws IOException
+ */
+ @RequestMapping("/exportExcel")
+ private String exportExcel(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+ String sessionMapID = request.getParameter(ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ Scratchie scratchie = (Scratchie) sessionMap.get(ScratchieConstants.ATTR_SCRATCHIE);
+
+ LinkedHashMap dataToExport = scratchieService.exportExcel(scratchie.getContentId());
+
+ String fileName = "scratchie_export.xlsx";
+ fileName = FileUtil.encodeFilenameForDownload(request, fileName);
+
+ response.setContentType("application/x-download");
+ response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
+
+ // set cookie that will tell JS script that export has been finished
+ String downloadTokenValue = WebUtil.readStrParam(request, "downloadTokenValue");
+ Cookie fileDownloadTokenCookie = new Cookie("fileDownloadToken", downloadTokenValue);
+ fileDownloadTokenCookie.setPath("/");
+ response.addCookie(fileDownloadTokenCookie);
+
+ // Code to generate file and write file contents to response
+ ServletOutputStream out = response.getOutputStream();
+ ExcelUtil.createExcel(out, dataToExport, null, false);
+
+ return null;
+ }
+
+ /**
+ * Get the mark summary with data arranged in bands. Can be displayed graphically or in a table.
+ */
+ @RequestMapping("/getMarkChartData")
+ private String getMarkChartData(HttpServletRequest request, HttpServletResponse res)
+ throws IOException, ServletException {
+
+ String sessionMapID = request.getParameter(ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ request.setAttribute(ScratchieConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID());
+
+ Scratchie scratchie = (Scratchie) sessionMap.get(ScratchieConstants.ATTR_SCRATCHIE);
+ List results = null;
+
+ if (scratchie != null) {
+ results = scratchieService.getMarksArray(scratchie.getContentId());
+ }
+
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ if (results != null) {
+ responseJSON.set("data", JsonUtil.readArray(results));
+ } else {
+ responseJSON.set("data", JsonUtil.readArray(new Float[0]));
+ }
+
+ res.setContentType("application/json;charset=utf-8");
+ res.getWriter().write(responseJSON.toString());
+ return null;
+
+ }
+
+ @RequestMapping("/statistic")
+ private String statistic(HttpServletRequest request) {
+
+ String sessionMapID = request.getParameter(ScratchieConstants.ATTR_SESSION_MAP_ID);
+ SessionMap sessionMap = (SessionMap) request.getSession()
+ .getAttribute(sessionMapID);
+ request.setAttribute(ScratchieConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID());
+
+ Scratchie scratchie = (Scratchie) sessionMap.get(ScratchieConstants.ATTR_SCRATCHIE);
+ if (scratchie != null) {
+ LeaderResultsDTO leaderDto = scratchieService.getLeaderResultsDTOForLeaders(scratchie.getContentId());
+ sessionMap.put("leaderDto", leaderDto);
+ }
+ return "pages/monitoring/parts/statisticpart";
+ }
+}
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/TblMonitorController.java
===================================================================
diff -u
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/TblMonitorController.java (revision 0)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/TblMonitorController.java (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -0,0 +1,266 @@
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+package org.lamsfoundation.lams.tool.scratchie.web.controller;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.log4j.Logger;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+import org.lamsfoundation.lams.tool.scratchie.ScratchieConstants;
+import org.lamsfoundation.lams.tool.scratchie.dto.BurningQuestionDTO;
+import org.lamsfoundation.lams.tool.scratchie.dto.BurningQuestionItemDTO;
+import org.lamsfoundation.lams.tool.scratchie.model.Scratchie;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieItem;
+import org.lamsfoundation.lams.tool.scratchie.model.ScratchieUser;
+import org.lamsfoundation.lams.tool.scratchie.service.IScratchieService;
+import org.lamsfoundation.lams.tool.scratchie.util.ScratchieItemComparator;
+import org.lamsfoundation.lams.util.AlphanumComparator;
+import org.lamsfoundation.lams.util.ExcelCell;
+import org.lamsfoundation.lams.util.ExcelUtil;
+import org.lamsfoundation.lams.util.FileUtil;
+import org.lamsfoundation.lams.util.WebUtil;
+import org.lamsfoundation.lams.web.util.AttributeNames;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+@Controller
+@RequestMapping("/tblmonitoring")
+public class TblMonitorController {
+ private static Logger log = Logger.getLogger(TblMonitorController.class);
+
+ @Autowired
+ @Qualifier("scratchieService")
+ private IScratchieService scratchieService;
+
+ /**
+ * Shows tra page
+ */
+ @RequestMapping("/tra")
+ public String tra(HttpServletRequest request) throws IOException, ServletException {
+
+ long toolContentId = WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_CONTENT_ID);
+ Scratchie scratchie = scratchieService.getScratchieByContentId(toolContentId);
+
+ int attemptedLearnersNumber = scratchieService.countUsersByContentId(toolContentId);
+ request.setAttribute("attemptedLearnersNumber", attemptedLearnersNumber);
+
+ Set items = new TreeSet<>(new ScratchieItemComparator());
+ items.addAll(scratchie.getScratchieItems());
+ request.setAttribute("items", items);
+
+ if (attemptedLearnersNumber != 0) {
+ // find first page in excel file
+ LinkedHashMap excelDoc = scratchieService.exportExcel(toolContentId);
+ ExcelCell[][] firstPageData = null;
+ for (String key : excelDoc.keySet()) {
+ firstPageData = excelDoc.get(key);
+ break;
+ }
+
+ int groupsSize = scratchieService.countSessionsByContentId(toolContentId);
+ ArrayList groupRows = new ArrayList<>();
+ for (int groupCount = 0; groupCount < groupsSize; groupCount++) {
+ ExcelCell[] groupRow = firstPageData[5 + groupCount];
+
+ String[] groupRow2 = new String[2];
+ groupRow2[0] = (String) groupRow[1].getCellValue();
+ groupRow2[1] = ((String) groupRow[groupRow.length - 1].getCellValue()).replaceAll("%", "");
+ groupRows.add(groupRow2);
+ }
+ request.setAttribute("groupRows", groupRows);
+ }
+
+ return "pages/tblmonitoring/tra";
+ }
+
+ /**
+ * Shows tra StudentChoices page
+ */
+ @RequestMapping("/traStudentChoices")
+ public String traStudentChoices(HttpServletRequest request) throws IOException, ServletException {
+
+ long toolContentId = WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_CONTENT_ID);
+ Scratchie scratchie = scratchieService.getScratchieByContentId(toolContentId);
+
+ //find second page in excel file
+ LinkedHashMap excelDoc = scratchieService.exportExcel(toolContentId);
+ ExcelCell[][] secondPageData = null;
+ for (ExcelCell[][] excelPage : excelDoc.values()) {
+ //check last row string starts with "*" (i.e. the string "*- Denotes the correct answer")
+ if (excelPage.length > 0) {
+ ExcelCell lastRow = excelPage[excelPage.length - 1][0];
+ if (lastRow != null && ((String) lastRow.getCellValue()).startsWith("*")) {
+ secondPageData = excelPage;
+ break;
+ }
+ }
+ }
+
+ ExcelCell[] correctAnswersRow = secondPageData[4];
+ request.setAttribute("correctAnswers", correctAnswersRow);
+
+ int groupsSize = scratchieService.countSessionsByContentId(toolContentId);
+ ArrayList groupRows = new ArrayList<>();
+ for (int groupCount = 0; groupCount < groupsSize; groupCount++) {
+ ExcelCell[] groupRow = secondPageData[6 + groupCount];
+ groupRows.add(groupRow);
+ }
+ request.setAttribute("groupRows", groupRows);
+
+ Set items = new TreeSet<>(new ScratchieItemComparator());
+ items.addAll(scratchie.getScratchieItems());
+ request.setAttribute("items", items);
+
+ request.setAttribute(AttributeNames.PARAM_TOOL_CONTENT_ID, toolContentId);
+ return "pages/tblmonitoring/traStudentChoices";
+ }
+
+ /**
+ * Exports tool results into excel.
+ *
+ * Had to move it from the tool as tool uses SessionMap
+ *
+ * @throws IOException
+ */
+ @RequestMapping("/exportExcel")
+ public String exportExcel(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+ Long toolContentId = WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_CONTENT_ID);
+ LinkedHashMap dataToExport = scratchieService.exportExcel(toolContentId);
+
+ String fileName = "scratchie_export.xlsx";
+ fileName = FileUtil.encodeFilenameForDownload(request, fileName);
+
+ response.setContentType("application/x-download");
+ response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
+
+ // Code to generate file and write file contents to response
+ ServletOutputStream out = response.getOutputStream();
+ ExcelUtil.createExcel(out, dataToExport, null, false);
+
+ return null;
+ }
+
+ /**
+ * Shows Teams page
+ *
+ * @throws JSONException
+ */
+ @RequestMapping(value = "/isBurningQuestionsEnabled", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ @ResponseBody
+ public String isBurningQuestionsEnabled(ActionMapping mapping, ActionForm form, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+
+ long toolContentId = WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_CONTENT_ID);
+ Scratchie scratchie = scratchieService.getScratchieByContentId(toolContentId);
+
+ // build JSON
+ ObjectNode responseJSON = JsonNodeFactory.instance.objectNode();
+ responseJSON.put("isBurningQuestionsEnabled", scratchie.isBurningQuestionsEnabled());
+ return responseJSON.toString();
+
+ }
+
+ /**
+ * Shows Teams page
+ */
+ @RequestMapping("/burningQuestions")
+ public String burningQuestions(HttpServletRequest request) throws IOException, ServletException {
+
+ long toolContentId = WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_CONTENT_ID);
+ Scratchie scratchie = scratchieService.getScratchieByContentId(toolContentId);
+
+ //find available burningQuestionDtos, if any
+ if (scratchie.isBurningQuestionsEnabled()) {
+ List burningQuestionItemDtos = scratchieService.getBurningQuestionDtos(scratchie,
+ null, true);
+
+ //unescape previously escaped session names
+ for (BurningQuestionItemDTO burningQuestionItemDto : burningQuestionItemDtos) {
+ List burningQuestionDtos = burningQuestionItemDto.getBurningQuestionDtos();
+
+ for (BurningQuestionDTO burningQuestionDto : burningQuestionItemDto.getBurningQuestionDtos()) {
+
+ String escapedBurningQuestion = StringEscapeUtils
+ .unescapeJavaScript(burningQuestionDto.getEscapedBurningQuestion());
+ burningQuestionDto.setEscapedBurningQuestion(escapedBurningQuestion);
+
+ String sessionName = StringEscapeUtils.unescapeJavaScript(burningQuestionDto.getSessionName());
+ burningQuestionDto.setSessionName(sessionName);
+ }
+
+ Collections.sort(burningQuestionDtos, new Comparator() {
+ @Override
+ public int compare(BurningQuestionDTO o1, BurningQuestionDTO o2) {
+ return new AlphanumComparator().compare(o1.getSessionName(), o2.getSessionName());
+ }
+ });
+ }
+
+ request.setAttribute(ScratchieConstants.ATTR_BURNING_QUESTION_ITEM_DTOS, burningQuestionItemDtos);
+ }
+
+ return "pages/tblmonitoring/burningQuestions";
+ }
+
+ /**
+ * Shows Teams page
+ */
+ @RequestMapping("/getModalDialogForTeamsTab")
+ public String getModalDialogForTeamsTab(HttpServletRequest request) throws IOException, ServletException {
+
+ long toolContentId = WebUtil.readLongParam(request, AttributeNames.PARAM_TOOL_CONTENT_ID);
+ Long userId = WebUtil.readLongParam(request, AttributeNames.PARAM_USER_ID);
+
+ ScratchieUser user = scratchieService.getUserByUserIDAndContentID(userId, toolContentId);
+ Collection scratchieItems = user == null ? new LinkedList<>()
+ : scratchieService.getItemsWithIndicatedScratches(user.getSession().getSessionId());
+
+ request.setAttribute("scratchieItems", scratchieItems);
+ return "pages/tblmonitoring/teams";
+ }
+}
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/AdminForm.java
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/AdminForm.java (.../AdminForm.java) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/AdminForm.java (.../AdminForm.java) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -20,34 +20,18 @@
* ****************************************************************
*/
-
package org.lamsfoundation.lams.tool.scratchie.web.form;
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.struts.action.ActionErrors;
-import org.apache.struts.action.ActionForm;
-import org.apache.struts.action.ActionMapping;
-import org.apache.struts.action.ActionMessage;
-import org.apache.struts.action.ActionMessages;
-
/**
*
*/
-public class AdminForm extends ActionForm {
+public class AdminForm {
private static final long serialVersionUID = 414425664356226L;
private boolean enabledExtraPointOption;
private String presetMarks;
- @Override
- public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
- ActionErrors ac = new ActionErrors();
- ac.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("this is an error"));
- return ac;
- }
-
public boolean isEnabledExtraPointOption() {
return enabledExtraPointOption;
}
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ReflectionForm.java
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ReflectionForm.java (.../ReflectionForm.java) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ReflectionForm.java (.../ReflectionForm.java) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -21,11 +21,9 @@
* ****************************************************************
*/
-
package org.lamsfoundation.lams.tool.scratchie.web.form;
import org.apache.log4j.Logger;
-import org.apache.struts.validator.ValidatorForm;
/**
*
@@ -34,7 +32,7 @@
*
*
*/
-public class ReflectionForm extends ValidatorForm {
+public class ReflectionForm {
private static final long serialVersionUID = -9054365604649146735L;
private static Logger logger = Logger.getLogger(ReflectionForm.class.getName());
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ScratchieForm.java
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ScratchieForm.java (.../ScratchieForm.java) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ScratchieForm.java (.../ScratchieForm.java) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -23,20 +23,15 @@
package org.lamsfoundation.lams.tool.scratchie.web.form;
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
-import org.apache.struts.action.ActionForm;
-import org.apache.struts.action.ActionMapping;
import org.lamsfoundation.lams.tool.scratchie.model.Scratchie;
/**
* Scratchie Form.
*
*
*/
-public class ScratchieForm extends ActionForm {
+public class ScratchieForm {
private static final long serialVersionUID = 3599879328307492312L;
private static Logger logger = Logger.getLogger(ScratchieForm.class.getName());
@@ -63,19 +58,6 @@
}
}
- @Override
- public void reset(ActionMapping mapping, HttpServletRequest request) {
- String param = mapping.getParameter();
- // if it is start page, all data read out from database or current session
- // so need not reset checkbox to refresh value!
- if (!StringUtils.equals(param, "start") && !StringUtils.equals(param, "initPage")) {
- scratchie.setDefineLater(false);
- scratchie.setReflectOnActivity(false);
- scratchie.setExtraPoint(false);
- scratchie.setBurningQuestionsEnabled(false);
- }
- }
-
public String getSessionMapID() {
return sessionMapID;
}
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ScratchieItemForm.java
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ScratchieItemForm.java (.../ScratchieItemForm.java) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ScratchieItemForm.java (.../ScratchieItemForm.java) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -20,18 +20,15 @@
* ****************************************************************
*/
-
package org.lamsfoundation.lams.tool.scratchie.web.form;
-import org.apache.struts.action.ActionForm;
-
/**
*
* Scratchie Item Form.
*
*
*/
-public class ScratchieItemForm extends ActionForm {
+public class ScratchieItemForm {
private static final long serialVersionUID = 3599879328307492312L;
private String itemIndex;
Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ScratchiePedagogicalPlannerForm.java
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ScratchiePedagogicalPlannerForm.java (.../ScratchiePedagogicalPlannerForm.java) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/form/ScratchiePedagogicalPlannerForm.java (.../ScratchiePedagogicalPlannerForm.java) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -20,16 +20,15 @@
* ****************************************************************
*/
-
package org.lamsfoundation.lams.tool.scratchie.web.form;
import org.lamsfoundation.lams.tool.scratchie.model.Scratchie;
-import org.lamsfoundation.lams.web.planner.PedagogicalPlannerActivityForm;
+import org.lamsfoundation.lams.web.planner.PedagogicalPlannerActivitySpringForm;
/**
*
*/
-public class ScratchiePedagogicalPlannerForm extends PedagogicalPlannerActivityForm {
+public class ScratchiePedagogicalPlannerForm extends PedagogicalPlannerActivitySpringForm {
private String instructions;
private String contentFolderID;
Index: lams_tool_scratchie/web/WEB-INF/spring-servlet.xml
===================================================================
diff -u
--- lams_tool_scratchie/web/WEB-INF/spring-servlet.xml (revision 0)
+++ lams_tool_scratchie/web/WEB-INF/spring-servlet.xml (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: lams_tool_scratchie/web/WEB-INF/tags/Page.tag
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/web/WEB-INF/tags/Page.tag (.../Page.tag) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/web/WEB-INF/tags/Page.tag (.../Page.tag) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -4,6 +4,7 @@
<%@ taglib uri="tags-lams" prefix="lams"%>
<%@ attribute name="type" required="true" rtexprvalue="true"%>
+<%@ attribute name="formID" required="false" rtexprvalue="true"%>
<%@ attribute name="style" required="false" rtexprvalue="true"%>
<%@ attribute name="title" required="false" rtexprvalue="true"%>
<%@ attribute name="titleHelpURL" required="false" rtexprvalue="true"%>
@@ -88,8 +89,8 @@
-
-
+
+
${toolForm.toolSessionID}
Index: lams_tool_scratchie/web/WEB-INF/tags/TextSearch.tag
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/web/WEB-INF/tags/TextSearch.tag (.../TextSearch.tag) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/web/WEB-INF/tags/TextSearch.tag (.../TextSearch.tag) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -32,12 +32,11 @@
<%@ tag body-content="scriptless" %>
<%@ taglib uri="tags-core" prefix="c" %>
<%@ taglib uri="tags-fmt" prefix="fmt" %>
-<%@ taglib uri="tags-html" prefix="html" %>
+<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="tags-lams" prefix="lams" %>
<%-- Required attributes --%>
<%@ attribute name="sessionMapID" required="true" rtexprvalue="true" %>
-<%@ attribute name="wrapInFormTag" required="true" rtexprvalue="true" %>
<%-- Optional attributes --%>
<%@ attribute name="action" required="false" rtexprvalue="true" %>
@@ -53,10 +52,10 @@
<%-- Default value for message key --%>
-
+
-
+
@@ -85,52 +84,40 @@
-
-
-
-
-
-
-
-
- |
-
-
- |
-
-
-
-
- |
-
-
- |
-
-
-
-
- |
-
-
- |
-
-
-
-
- |
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ |
+
+
+ |
+
+
+
+
+ |
+
+
+ |
+
+
+
+
+ |
+
+
+ |
+
+
+
+
+ |
+
+
+ |
+
+
\ No newline at end of file
Index: lams_tool_scratchie/web/WEB-INF/web.xml
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/web/WEB-INF/web.xml (.../web.xml) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/web/WEB-INF/web.xml (.../web.xml) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -79,25 +79,11 @@
- action
- org.apache.struts.action.ActionServlet
-
- config
- /WEB-INF/struts-config.xml
-
-
- debug
- 999
-
-
- detail
- 1
-
-
- validate
- true
-
- 2
+ spring
+
+ org.springframework.web.servlet.DispatcherServlet
+
+ 1
@@ -128,7 +114,7 @@
- action
+ spring
*.do
Index: lams_tool_scratchie/web/common/messages.jsp
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/web/common/messages.jsp (.../messages.jsp) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/web/common/messages.jsp (.../messages.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -1,8 +1,9 @@
<%-- Error Messages --%>
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: lams_tool_scratchie/web/common/taglibs.jsp
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/web/common/taglibs.jsp (.../taglibs.jsp) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/web/common/taglibs.jsp (.../taglibs.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -1,13 +1,10 @@
<%@ page language="java" errorPage="/error.jsp" pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %>
-<%@ taglib uri="tags-tiles" prefix="tiles" %>
-<%@ taglib uri="tags-bean" prefix="bean" %>
-<%@ taglib uri="tags-html" prefix="html" %>
-<%@ taglib uri="tags-logic" prefix="logic" %>
<%@ taglib uri="tags-function" prefix="fn" %>
<%@ taglib uri="tags-core" prefix="c" %>
<%@ taglib uri="tags-fmt" prefix="fmt" %>
<%@ taglib uri="tags-xml" prefix="x" %>
<%@ taglib uri="tags-lams" prefix="lams" %>
+<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
Index: lams_tool_scratchie/web/pages/admin/config.jsp
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/web/pages/admin/config.jsp (.../config.jsp) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/web/pages/admin/config.jsp (.../config.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -13,7 +13,7 @@
-
+
<%@ include file="/common/messages.jsp"%>
@@ -23,19 +23,18 @@
-
+
-
-
+
-
-
+
+
Index: lams_tool_scratchie/web/pages/authoring/advance.jsp
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/web/pages/authoring/advance.jsp (.../advance.jsp) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/web/pages/authoring/advance.jsp (.../advance.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -32,37 +32,37 @@
@@ -85,11 +85,11 @@
/>
-
+
- ${confidenceProvidingActivity.title}
+ ${confidenceProvidingActivity.title}
-
+
@@ -115,7 +115,7 @@
@@ -124,13 +124,13 @@
-
+
Index: lams_tool_scratchie/web/pages/authoring/parts/additem.jsp
===================================================================
diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1
--- lams_tool_scratchie/web/pages/authoring/parts/additem.jsp (.../additem.jsp) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80)
+++ lams_tool_scratchie/web/pages/authoring/parts/additem.jsp (.../additem.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1)
@@ -220,16 +220,15 @@
<%@ include file="/common/messages.jsp"%>
-
-
-
-
-
-
+
+
+
+
+
-
+