Index: lams_central/conf/language/lams/ApplicationResources_en_AU.properties =================================================================== diff -u -r99d2b23140678283fd284b18e7aeeb24d93e0534 -rf9c66e78afa51f175afcaf22ee81f9b3460afea8 --- lams_central/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision 99d2b23140678283fd284b18e7aeeb24d93e0534) +++ lams_central/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision f9c66e78afa51f175afcaf22ee81f9b3460afea8) @@ -880,6 +880,7 @@ authoring.tbl.desc.ae =State the questions for AE. Click "Create Question" to add more questions. #QB questions# +label.create.question =Create question label.question.type.multiple.choice =Multiple choice label.question.type.matching.pairs =Matching pairs label.question.type.short.answer =Short answer Fisheye: Tag f9c66e78afa51f175afcaf22ee81f9b3460afea8 refers to a dead (removed) revision in file `lams_central/src/java/org/lamsfoundation/lams/web/controller/SearchQBController.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_central/src/java/org/lamsfoundation/lams/web/qb/EditQbQuestionController.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/web/qb/EditQbQuestionController.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/web/qb/EditQbQuestionController.java (revision f9c66e78afa51f175afcaf22ee81f9b3460afea8) @@ -0,0 +1,293 @@ +package org.lamsfoundation.lams.web.qb; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +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.math.NumberUtils; +import org.apache.log4j.Logger; +import org.lamsfoundation.lams.qb.QbConstants; +import org.lamsfoundation.lams.qb.form.QbQuestionForm; +import org.lamsfoundation.lams.qb.model.QbCollection; +import org.lamsfoundation.lams.qb.model.QbOption; +import org.lamsfoundation.lams.qb.model.QbQuestion; +import org.lamsfoundation.lams.qb.model.QbQuestionUnit; +import org.lamsfoundation.lams.qb.service.IQbService; +import org.lamsfoundation.lams.qb.service.QbUtils; +import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; +import org.lamsfoundation.lams.util.CommonConstants; +import org.lamsfoundation.lams.util.Configuration; +import org.lamsfoundation.lams.util.ConfigurationKeys; +import org.lamsfoundation.lams.util.FileUtil; +import org.lamsfoundation.lams.util.MessageService; +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.stereotype.Controller; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.WebApplicationContext; + +@Controller +@RequestMapping("/qb/edit") +public class EditQbQuestionController { + private static Logger log = Logger.getLogger(EditQbQuestionController.class); + + @Autowired + @Qualifier("centralMessageService") + private MessageService messageService; + @Autowired + private IQbService qbService; + @Autowired + private IUserManagementService userManagementService; + @Autowired + WebApplicationContext applicationcontext; + + /** + * Display empty page for new assessment question. + */ + @RequestMapping("/newQuestionInit") + public String newQuestionInit(HttpServletRequest request, HttpServletResponse response, + @RequestParam Long collectionUid) throws ServletException, IOException { + + //TODO think about where do we need to get ContentFolderID, and whether it's a good idea to generate a new one each time + String contentFolderID = FileUtil.generateUniqueContentFolderID(); + + QbQuestionForm questionForm = new QbQuestionForm(); + //we need to set form as a request attribute, as long as we use jsps from another context from the Assessment tool + request.setAttribute("assessmentQuestionForm", questionForm); + questionForm.setDisplayOrder(-1);//which signifies it's a new question + questionForm.setContentFolderID(contentFolderID); + questionForm.setMaxMark("1"); + questionForm.setPenaltyFactor("0"); + questionForm.setAnswerRequired(true); + questionForm.setCollectionUid(collectionUid); + + List optionList = new ArrayList<>(); + for (int i = 0; i < QbConstants.INITIAL_OPTIONS_NUMBER; i++) { + QbOption option = new QbOption(); + option.setDisplayOrder(i + 1); + optionList.add(option); + } + request.setAttribute(QbConstants.ATTR_OPTION_LIST, optionList); + + List unitList = new ArrayList<>(); + QbQuestionUnit unit = new QbQuestionUnit(); + unit.setDisplayOrder(1); + unit.setMultiplier(1); + unitList.add(unit); + for (int i = 1; i < QbConstants.INITIAL_UNITS_NUMBER; i++) { + unit = new QbQuestionUnit(); + unit.setDisplayOrder(i + 1); + unit.setMultiplier(0); + unitList.add(unit); + } + request.setAttribute(QbConstants.ATTR_UNIT_LIST, unitList); + + Integer type = NumberUtils.toInt(request.getParameter(QbConstants.ATTR_QUESTION_TYPE)); +// sessionMap.put(QbConstants.ATTR_QUESTION_TYPE, type); + request.setAttribute(AttributeNames.PARAM_CONTENT_FOLDER_ID, contentFolderID); + + String jspPageName = getAuthoringJspByQuestionType(type); + forwardToAssessmentJsp(jspPageName, request, response); + return null; + } + + /** + * Display edit page for existed assessment question. + */ + @RequestMapping("/editQuestion") + public String editQuestion(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + Long qbQuestionUid = WebUtil.readLongParam(request, "qbQuestionUid"); + QbQuestion qbQuestion = qbService.getQbQuestionByUid(qbQuestionUid); + if (qbQuestion == null) { + throw new RuntimeException("QbQuestion with uid:" + qbQuestionUid + " was not found!"); + } + QbQuestionForm questionForm = new QbQuestionForm(); + //we need to set form as a request attribute, as long as we use jsps from another context from the Assessment tool + request.setAttribute("assessmentQuestionForm", questionForm); + QbUtils.fillFormWithQbQuestion(qbQuestion, questionForm, request); + + //store uid as displayOrder in order to use it later during question saving + questionForm.setDisplayOrder(qbQuestionUid.intValue()); + + //TODO think about where do we need to get ContentFolderID, and whether it's a good idea to generate a new one each time + String contentFolderID = FileUtil.generateUniqueContentFolderID(); + questionForm.setContentFolderID(contentFolderID); + request.setAttribute(AttributeNames.PARAM_CONTENT_FOLDER_ID, contentFolderID); + + String jspPageName = getAuthoringJspByQuestionType(qbQuestion.getType()); + forwardToAssessmentJsp(jspPageName, request, response); + return null; + } + + /** + * 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. + * @throws IOException + */ + @RequestMapping("/saveOrUpdateQuestion") + @ResponseBody + public String saveOrUpdateQuestion(@ModelAttribute("assessmentQuestionForm") QbQuestionForm questionForm, + HttpServletRequest request, HttpServletResponse response) throws IOException { + //find according question + QbQuestion qbQuestion = null; + // add + if (questionForm.getDisplayOrder() == -1) { + qbQuestion = new QbQuestion(); + qbQuestion.setType(questionForm.getQuestionType()); + + // edit + } else { + Long qbQuestionUid = Long.valueOf(questionForm.getDisplayOrder()); + qbQuestion = qbService.getQbQuestionByUid(qbQuestionUid); + } + + boolean IS_AUTHORING_RESTRICTED = false; + int isQbQuestionModified = QbUtils.extractFormToQbQuestion(qbQuestion, questionForm, request, qbService, + IS_AUTHORING_RESTRICTED); + switch (isQbQuestionModified) { + case IQbService.QUESTION_MODIFIED_VERSION_BUMP: { + // new version of the old question gets created + qbQuestion = qbQuestion.clone(); + qbQuestion.clearID(); + qbQuestion.setVersion(qbService.getMaxQuestionVersion(qbQuestion.getQuestionId())); + qbQuestion.setCreateDate(new Date()); + } + break; + case IQbService.QUESTION_MODIFIED_ID_BUMP: { + // new question gets created + qbQuestion = qbQuestion.clone(); + qbQuestion.clearID(); + qbQuestion.setVersion(1); + qbQuestion.setQuestionId(qbService.getMaxQuestionId()+1); + qbQuestion.setCreateDate(new Date()); + + } + break; + } + userManagementService.save(qbQuestion); + + //in case of new question - add it to specified collection + if (questionForm.getDisplayOrder() == -1) { + Long collectionUid = WebUtil.readLongParam(request, "collectionUid"); + qbService.addQuestionToCollection(collectionUid, qbQuestion.getUid(), false); + } + + // add question case - return nothing + if (questionForm.getDisplayOrder() == -1) { + return null; + + // edit question case - return question's uid + } else { + response.setContentType("text/plain"); + response.setCharacterEncoding("UTF-8"); + return qbQuestion.getUid().toString(); + } + } + + /** + * Ajax call, will add one more input line for new resource item instruction. + */ + @RequestMapping("/addOption") + public String addOption(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + TreeSet optionList = QbUtils.getOptionsFromRequest(qbService, request, false); + QbOption option = new QbOption(); + int maxSeq = 1; + if ((optionList != null) && (optionList.size() > 0)) { + QbOption last = optionList.last(); + maxSeq = last.getDisplayOrder() + 1; + } + option.setDisplayOrder(maxSeq); + optionList.add(option); + + request.setAttribute(AttributeNames.PARAM_CONTENT_FOLDER_ID, + WebUtil.readStrParam(request, AttributeNames.PARAM_CONTENT_FOLDER_ID)); + request.setAttribute(QbConstants.ATTR_QUESTION_TYPE, + WebUtil.readIntParam(request, QbConstants.ATTR_QUESTION_TYPE)); + request.setAttribute(QbConstants.ATTR_OPTION_LIST, optionList); + forwardToAssessmentJsp("optionlist.jsp", request, response); + return null; + } + + /** + * Ajax call, will add one more input line for new Unit. + */ + @RequestMapping("/newUnit") + public String newUnit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + TreeSet unitList = QbUtils.getUnitsFromRequest(qbService, request, false); + QbQuestionUnit unit = new QbQuestionUnit(); + int maxSeq = 1; + if ((unitList != null) && (unitList.size() > 0)) { + QbQuestionUnit last = unitList.last(); + maxSeq = last.getDisplayOrder() + 1; + } + unit.setDisplayOrder(maxSeq); + unitList.add(unit); + + request.setAttribute(QbConstants.ATTR_UNIT_LIST, unitList); + forwardToAssessmentJsp("unitlist.jsp", request, response); + return null; + } + + /** + * Get back jsp name. + */ + private static String getAuthoringJspByQuestionType(Integer type) { + String jspName; + switch (type) { + case QbQuestion.TYPE_MULTIPLE_CHOICE: + jspName = "addmultiplechoice.jsp"; + break; + case QbQuestion.TYPE_MATCHING_PAIRS: + jspName = "addmatchingpairs.jsp"; + break; + case QbQuestion.TYPE_SHORT_ANSWER: + jspName = "addshortanswer.jsp"; + break; + case QbQuestion.TYPE_NUMERICAL: + jspName = "addnumerical.jsp"; + break; + case QbQuestion.TYPE_TRUE_FALSE: + jspName = "addtruefalse.jsp"; + break; + case QbQuestion.TYPE_ESSAY: + jspName = "addessay.jsp"; + break; + case QbQuestion.TYPE_ORDERING: + jspName = "addordering.jsp"; + break; + case QbQuestion.TYPE_MARK_HEDGING: + jspName = "addmarkhedging.jsp"; + break; + default: + jspName = null; + break; + } + return jspName; + } + + /** + * Forwards to the specified jsp page from Assessment tool. + */ + private void forwardToAssessmentJsp(String jspPageName, HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String serverURLContextPath = Configuration.get(ConfigurationKeys.SERVER_URL_CONTEXT_PATH); + serverURLContextPath = serverURLContextPath.startsWith("/") ? serverURLContextPath : "/" + serverURLContextPath; + serverURLContextPath += serverURLContextPath.endsWith("/") ? "" : "/"; + applicationcontext.getServletContext().getContext(serverURLContextPath + "tool/" + CommonConstants.TOOL_SIGNATURE_ASSESSMENT) + .getRequestDispatcher("/pages/authoring/parts/" + jspPageName +"?lessonID=1").forward(request, response); + } +} Index: lams_central/src/java/org/lamsfoundation/lams/web/qb/SearchQBController.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/web/qb/SearchQBController.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/web/qb/SearchQBController.java (revision f9c66e78afa51f175afcaf22ee81f9b3460afea8) @@ -0,0 +1,207 @@ +/**************************************************************** + * 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.web.qb; + +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.lamsfoundation.lams.qb.model.QbQuestion; +import org.lamsfoundation.lams.qb.service.IQbService; +import org.lamsfoundation.lams.tool.Tool; +import org.lamsfoundation.lams.tool.ToolAccessMode; +import org.lamsfoundation.lams.tool.ToolContent; +import org.lamsfoundation.lams.tool.exception.DataMissingException; +import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; +import org.lamsfoundation.lams.util.CommonConstants; +import org.lamsfoundation.lams.util.WebUtil; +import org.lamsfoundation.lams.web.util.AttributeNames; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.util.HtmlUtils; + +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * @author Andrey Balan + */ +@Controller +@RequestMapping("/searchQB") +public class SearchQBController { + + private static Logger log = Logger.getLogger(SearchQBController.class); + + public static final String PARAM_SEARCH = "_search"; + public static final String PARAM_SEARCH_FIELD = "searchField"; + public static final String PARAM_SEARCH_OPERATION = "searchOper"; + public static final String PARAM_SEARCH_STRING = "searchString"; + + @Autowired + private IQbService qbService; + + @Autowired + private IUserManagementService userManagementService; + + @RequestMapping("/start") + private String start(HttpServletRequest request) { + ToolAccessMode mode = WebUtil.readToolAccessModeAuthorDefaulted(request); + request.setAttribute(AttributeNames.ATTR_MODE, mode.toString()); + + Long toolContentId = WebUtil.readLongParam(request, "toolContentId"); + ToolContent toolContent = (ToolContent) userManagementService.findById(ToolContent.class, toolContentId); + if (toolContent == null) { + String error = "The toolContentID " + toolContentId + + " is not valid. No such record exists on the database."; + throw new DataMissingException(error); + } + Tool tool = toolContent.getTool(); + String toolSignature = tool.getToolSignature(); + + //empty questionTypesAvailable means no other questionTypes available for this tool + StringBuilder questionTypesAvailable = new StringBuilder(); + //by default show MCQ type of questions (except for Q&A tool) + int questionTypeDefault = QbQuestion.TYPE_MULTIPLE_CHOICE; + if (CommonConstants.TOOL_SIGNATURE_SCRATCHIE.equals(toolSignature) || CommonConstants.TOOL_SIGNATURE_MCQ.equals(toolSignature)) { + + //CommonConstants.TOOL_SIGNATURE_SURVEY + } else if ("lasurv11".equals(toolSignature)) { + questionTypesAvailable.append(QbQuestion.TYPE_MULTIPLE_CHOICE); + questionTypesAvailable.append(","); + questionTypesAvailable.append(QbQuestion.TYPE_ESSAY); + + } else if (CommonConstants.TOOL_SIGNATURE_ASSESSMENT.equals(toolSignature)) { + questionTypesAvailable.append(QbQuestion.TYPE_MULTIPLE_CHOICE); + questionTypesAvailable.append(","); + questionTypesAvailable.append(QbQuestion.TYPE_MATCHING_PAIRS); + questionTypesAvailable.append(","); + questionTypesAvailable.append(QbQuestion.TYPE_SHORT_ANSWER); + questionTypesAvailable.append(","); + questionTypesAvailable.append(QbQuestion.TYPE_NUMERICAL); + questionTypesAvailable.append(","); + questionTypesAvailable.append(QbQuestion.TYPE_TRUE_FALSE); + questionTypesAvailable.append(","); + questionTypesAvailable.append(QbQuestion.TYPE_ESSAY); + questionTypesAvailable.append(","); + questionTypesAvailable.append(QbQuestion.TYPE_ORDERING); + questionTypesAvailable.append(","); + questionTypesAvailable.append(QbQuestion.TYPE_MARK_HEDGING); + + //CommonConstants.TOOL_SIGNATURE_QA + } else if ("laqa11".equals(toolSignature)) { + questionTypeDefault = QbQuestion.TYPE_ESSAY; + } + request.setAttribute("questionType", questionTypeDefault); + request.setAttribute("questionTypesAvailable", questionTypesAvailable.toString()); + + return "qb/search"; + } + + /** + * Returns an xml representation of the lesson grid for a course for gradebook + * + * This has two modes, learnerView and monitorView + * + * Learner view will get the data specific to one user + * + * Monitor will get the data average for whole lessons. + */ + @RequestMapping("/getPagedQuestions") + @ResponseBody + private String getPagedQuestions(HttpServletRequest request, HttpServletResponse response) { + + Integer questionType = WebUtil.readIntParam(request, "questionType"); +// boolean isPrivate = WebUtil.readBooleanParam(request, "isPrivate"); + + // Getting the params passed in from the jqGrid + int page = WebUtil.readIntParam(request, CommonConstants.PARAM_PAGE); + int rowLimit = WebUtil.readIntParam(request, CommonConstants.PARAM_ROWS); + String sortOrder = WebUtil.readStrParam(request, CommonConstants.PARAM_SORD); + String sortBy = WebUtil.readStrParam(request, CommonConstants.PARAM_SIDX, true); + if (StringUtils.isEmpty(sortBy)) { + sortBy = "userName"; + } + String searchString = WebUtil.readStrParam(request, "searchString", true); + + // Get the user list from the db + List questions = qbService.getPagedQbQuestions(questionType, page - 1, rowLimit, sortBy, sortOrder, + searchString); + int countQuestions = qbService.getCountQbQuestions(questionType, searchString); + int totalPages = Double.valueOf(Math.ceil(Double.valueOf(countQuestions) / Double.valueOf(rowLimit))) + .intValue(); + + ArrayNode rows = JsonNodeFactory.instance.arrayNode(); + int i = 1; + for (QbQuestion question : questions) { + + ArrayNode questionData = JsonNodeFactory.instance.arrayNode(); + questionData.add(question.getUid()); + String title = question.getName() == null ? "" : question.getName().replaceAll("\\<.*?\\>", "").replaceAll("\\n", " ").trim(); + questionData.add(HtmlUtils.htmlEscape(title)); + String description = question.getDescription() == null ? "" : question.getDescription().replaceAll("\\<.*?\\>", "").trim(); + questionData.add(HtmlUtils.htmlEscape(description)); + + ObjectNode userRow = JsonNodeFactory.instance.objectNode(); + userRow.put("id", i++); + userRow.set("cell", questionData); + + rows.add(userRow); + } + + ObjectNode responseJSON = JsonNodeFactory.instance.objectNode(); + responseJSON.put("total", totalPages); + responseJSON.put("page", page); + responseJSON.put("records", countQuestions); + responseJSON.set("rows", rows); + + response.setContentType("application/json;charset=utf-8"); + return responseJSON.toString(); + } + + @RequestMapping("/displayQuestionDetails") + private String displayQuestionDetails(HttpServletRequest request) { + Long questionUid = WebUtil.readLongParam(request, "questionUid"); + QbQuestion qbQuestion = (QbQuestion) userManagementService.findById(QbQuestion.class, questionUid); + request.setAttribute("question", qbQuestion); + + List otherVersions = qbService.getQbQuestionsByQuestionId(qbQuestion.getQuestionId()); + request.setAttribute("otherVersions", otherVersions); + + return "qb/qbQuestionDetails"; + } + + @RequestMapping("/addQuestion") + private String addQuestion(HttpServletRequest request) { + Long questionUid = WebUtil.readLongParam(request, "questionUid"); + Long toolContentId = WebUtil.readLongParam(request, "toolContentId"); + + return "qb/"; + } +} \ No newline at end of file Index: lams_central/web/qb/collection.jsp =================================================================== diff -u -r0eb673cae894e38f77a6470467da5f26111b3862 -rf9c66e78afa51f175afcaf22ee81f9b3460afea8 --- lams_central/web/qb/collection.jsp (.../collection.jsp) (revision 0eb673cae894e38f77a6470467da5f26111b3862) +++ lams_central/web/qb/collection.jsp (.../collection.jsp) (revision f9c66e78afa51f175afcaf22ee81f9b3460afea8) @@ -11,6 +11,7 @@ +