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"%> - - - - - - + + + + +
- +
@@ -244,7 +243,7 @@
- +
Index: lams_tool_scratchie/web/pages/authoring/pedagogicalPlannerForm.jsp =================================================================== diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1 --- lams_tool_scratchie/web/pages/authoring/pedagogicalPlannerForm.jsp (.../pedagogicalPlannerForm.jsp) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80) +++ lams_tool_scratchie/web/pages/authoring/pedagogicalPlannerForm.jsp (.../pedagogicalPlannerForm.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1) @@ -45,7 +45,7 @@ <%@ include file="/common/messages.jsp"%> - + Index: lams_tool_scratchie/web/pages/learning/notebook.jsp =================================================================== diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r22cca1a815f7aee530e14ab87b7744f381d03ac1 --- lams_tool_scratchie/web/pages/learning/notebook.jsp (.../notebook.jsp) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80) +++ lams_tool_scratchie/web/pages/learning/notebook.jsp (.../notebook.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1) @@ -22,7 +22,7 @@ - + Index: lams_tool_scribe/web/pages/authoring/authoring.jsp =================================================================== diff -u -r770fb82629331ca1c70c47845ab63b9528b7bc9e -r22cca1a815f7aee530e14ab87b7744f381d03ac1 --- lams_tool_scribe/web/pages/authoring/authoring.jsp (.../authoring.jsp) (revision 770fb82629331ca1c70c47845ab63b9528b7bc9e) +++ lams_tool_scribe/web/pages/authoring/authoring.jsp (.../authoring.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1) @@ -25,7 +25,7 @@ - + Index: lams_tool_scribe/web/pages/authoring/headingForm.jsp =================================================================== diff -u -r770fb82629331ca1c70c47845ab63b9528b7bc9e -r22cca1a815f7aee530e14ab87b7744f381d03ac1 --- lams_tool_scribe/web/pages/authoring/headingForm.jsp (.../headingForm.jsp) (revision 770fb82629331ca1c70c47845ab63b9528b7bc9e) +++ lams_tool_scribe/web/pages/authoring/headingForm.jsp (.../headingForm.jsp) (revision 22cca1a815f7aee530e14ab87b7744f381d03ac1) @@ -40,7 +40,7 @@ } - +