Index: lams_build/lib/lams/lams.jar =================================================================== diff -u -r76f496c7c7d8cafecbe2b6c0a85a408c60d568b8 -r33a5ad24fad4e3e2d3d8f0c67d88ccb5167affe7 Binary files differ Index: lams_common/src/java/org/lamsfoundation/lams/util/ValidationUtil.java =================================================================== diff -u -re66bf3aa8146d04b064b169513609b2a28a35149 -r33a5ad24fad4e3e2d3d8f0c67d88ccb5167affe7 --- lams_common/src/java/org/lamsfoundation/lams/util/ValidationUtil.java (.../ValidationUtil.java) (revision e66bf3aa8146d04b064b169513609b2a28a35149) +++ lams_common/src/java/org/lamsfoundation/lams/util/ValidationUtil.java (.../ValidationUtil.java) (revision 33a5ad24fad4e3e2d3d8f0c67d88ccb5167affe7) @@ -26,6 +26,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.lang.StringUtils; + /** * Utility methods for String validation. */ @@ -109,4 +111,33 @@ Matcher m = pattern.matcher(input.trim()); return m.matches(); } + + /** + * Checks whether min words limit is reached + * + * @param text + * @param minWordsLimit + * @param isDerivedFromCKEditor whether the text was acquired from CKEditor + * @return + */ + public static boolean isMinWordsLimitReached(String text, int minWordsLimit, boolean isDerivedFromCKEditor) { + + if (minWordsLimit <= 0) { + return true; + } else if (StringUtils.isBlank(text)) { + return false; + } + + // HTML tags stripping + if (isDerivedFromCKEditor) { + text = WebUtil.removeHTMLtags(text); // text.replaceAll("/<\/?[a-z][^>]*>/gi", ''); + } else { + text = text.replaceAll("(?:
)", " "); + } + + int wordCount = (text.length() == 0) ? 0 : text.replaceAll("[\'\";:,\\.\\?\\-!]+", "").trim().split("\\S+").length;//.match(/\S+/g) || []) ; + + // check min words limit is reached + return (wordCount >= minWordsLimit); + } } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/action/LearningAction.java =================================================================== diff -u -r57dfab8f97ff1bad83bc50605c6dce9b8ef4e4f3 -r33a5ad24fad4e3e2d3d8f0c67d88ccb5167affe7 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/action/LearningAction.java (.../LearningAction.java) (revision 57dfab8f97ff1bad83bc50605c6dce9b8ef4e4f3) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/action/LearningAction.java (.../LearningAction.java) (revision 33a5ad24fad4e3e2d3d8f0c67d88ccb5167affe7) @@ -76,6 +76,7 @@ import org.lamsfoundation.lams.tool.assessment.web.form.ReflectionForm; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; import org.lamsfoundation.lams.util.DateUtil; +import org.lamsfoundation.lams.util.ValidationUtil; import org.lamsfoundation.lams.util.WebUtil; import org.lamsfoundation.lams.web.session.SessionManager; import org.lamsfoundation.lams.web.util.AttributeNames; @@ -886,18 +887,12 @@ && (question.getMinWordsLimit() > 0)) { String answer = new String(question.getAnswerString()); - - // HTML tags stripping - if (question.isAllowRichEditor()) { - answer = WebUtil.removeHTMLtags(answer); // answer.replaceAll("/<\/?[a-z][^>]*>/gi", ''); - } else { - answer = answer.replaceAll("(?:
)", " "); - } - - int wordCount = (answer.length() == 0) ? 0 : answer.replaceAll("[\'\";:,\\.\\?\\-!]+", "").split("\\S+").length;//.match(/\S+/g) || []) ; + boolean isMinWordsLimitReached = ValidationUtil.isMinWordsLimitReached(answer, + question.getMinWordsLimit(), question.isAllowRichEditor()); + // check min words limit is reached - if (wordCount < question.getMinWordsLimit()) { + if (!isMinWordsLimitReached) { isAllQuestionsReachedMinWordsLimit = false; break; } Index: lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/web/QaLearningAction.java =================================================================== diff -u -r40937c574021c496d91487979b905b23a193b766 -r33a5ad24fad4e3e2d3d8f0c67d88ccb5167affe7 --- lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/web/QaLearningAction.java (.../QaLearningAction.java) (revision 40937c574021c496d91487979b905b23a193b766) +++ lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/web/QaLearningAction.java (.../QaLearningAction.java) (revision 33a5ad24fad4e3e2d3d8f0c67d88ccb5167affe7) @@ -75,6 +75,7 @@ import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; import org.lamsfoundation.lams.util.DateUtil; +import org.lamsfoundation.lams.util.ValidationUtil; import org.lamsfoundation.lams.util.WebUtil; import org.lamsfoundation.lams.web.action.LamsDispatchAction; import org.lamsfoundation.lams.web.session.SessionManager; @@ -151,42 +152,36 @@ boolean isTestHarness = Boolean.valueOf(request.getParameter("testHarness")); String answerParamName = "answer" + questionIndex + (isTestHarness ? "__textarea" : ""); String answer = request.getParameter(answerParamName); - // String answerPresentable = QaUtils.replaceNewLines(answer); Integer questionIndexInteger = new Integer(questionIndex); mapAnswers.put(questionIndexInteger.toString(), answer); mapAnswersPresentable.put(questionIndexInteger.toString(), answer); + + //validate + ActionMessages newErrors = validateQuestionAnswer(answer, questionIndexInteger, generalLearnerFlowDTO); + errors.add(newErrors); - Map questionMap = generalLearnerFlowDTO.getMapQuestionContentLearner(); - QaQuestionDTO dto = questionMap.get(questionIndexInteger); - if (dto.isRequired() && isEmpty(answer)) { - errors.add(Globals.ERROR_KEY, new ActionMessage("error.required", questionIndexInteger)); - forwardName = QaAppConstants.LOAD_LEARNER; - } else { - // store + // store + if (errors.isEmpty()) { QaLearningAction.qaService.updateResponseWithNewAnswer(answer, toolSessionID, new Long( questionIndex)); } } - saveErrors(request, errors); } else { - mapAnswers = storeSequentialAnswer(qaLearningForm, request, generalLearnerFlowDTO, true); + Object[] results = storeSequentialAnswer(qaLearningForm, request, generalLearnerFlowDTO, true); + mapAnswers = (Map) results[0]; + errors = (ActionMessages) results[1]; mapAnswersPresentable = (Map) sessionMap.get(QaAppConstants.MAP_ALL_RESULTS_KEY); mapAnswersPresentable = QaLearningAction.removeNewLinesMap(mapAnswersPresentable); - - // only need to check the final question as the others will have been checked when the user clicked next. - Map questionMap = generalLearnerFlowDTO.getMapQuestionContentLearner(); - int numQuestions = questionMap.size(); - Integer finalQuestionIndex = new Integer(numQuestions); - QaQuestionDTO dto = questionMap.get(finalQuestionIndex); - if (dto.isRequired() && isEmpty(mapAnswersPresentable.get(finalQuestionIndex.toString()))) { - errors.add(Globals.ERROR_KEY, new ActionMessage("error.required", finalQuestionIndex)); - forwardName = QaAppConstants.LOAD_LEARNER; - } } + if (!errors.isEmpty()) { + saveErrors(request, errors); + forwardName = QaAppConstants.LOAD_LEARNER; + } + //in case noReeditAllowed finalize response so user can't refresh the page and post answers again if (errors.isEmpty() && qaContent.isNoReeditAllowed()) { qaQueUsr.setResponseFinalized(true); @@ -572,15 +567,15 @@ * @param getNextQuestion * @return */ - private Map storeSequentialAnswer(ActionForm form, HttpServletRequest request, + private Object[] storeSequentialAnswer(ActionForm form, HttpServletRequest request, GeneralLearnerFlowDTO generalLearnerFlowDTO, boolean getNextQuestion) { QaLearningForm qaLearningForm = (QaLearningForm) form; String httpSessionID = qaLearningForm.getHttpSessionID(); SessionMap sessionMap = (SessionMap) request.getSession().getAttribute(httpSessionID); String currentQuestionIndex = qaLearningForm.getCurrentQuestionIndex(); - Map mapAnswers = (Map) sessionMap.get(QaAppConstants.MAP_ALL_RESULTS_KEY); + Map mapAnswers = (Map) sessionMap.get(QaAppConstants.MAP_ALL_RESULTS_KEY); if (mapAnswers == null) { mapAnswers = new TreeMap(new QaComparator()); } @@ -595,22 +590,19 @@ int nextQuestionOffset = getNextQuestion ? 1 : -1; - // is this question required and are they trying to go to the next question? - // if so, check if the answer is blank and generate an error if it is blank. - Map questionContentMap = generalLearnerFlowDTO.getMapQuestionContentLearner(); - QaQuestionDTO dto = questionContentMap.get(new Integer(currentQuestionIndex)); - boolean isRequiredQuestionMissed = dto.isRequired() && isEmpty(newAnswer); - if (getNextQuestion && isRequiredQuestionMissed) { - ActionMessages errors = new ActionMessages(); - errors.add(Globals.ERROR_KEY, new ActionMessage("error.required", currentQuestionIndex)); - saveErrors(request, errors); - nextQuestionOffset = 0; + // validation only if trying to go to the next question + ActionMessages errors = new ActionMessages(); + if (getNextQuestion) { + errors = validateQuestionAnswer(newAnswer, new Integer(currentQuestionIndex), generalLearnerFlowDTO); } // store - if (!isRequiredQuestionMissed) { + if (errors.isEmpty()) { QaLearningAction.qaService.updateResponseWithNewAnswer(newAnswer, qaLearningForm.getToolSessionID(), new Long(currentQuestionIndex)); + } else { + saveErrors(request, errors); + nextQuestionOffset = 0; } sessionMap.put(QaAppConstants.MAP_ALL_RESULTS_KEY, mapAnswers); @@ -651,9 +643,31 @@ request.setAttribute(QaAppConstants.GENERAL_LEARNER_FLOW_DTO, generalLearnerFlowDTO); - return mapSequentialAnswers; + return new Object[]{mapSequentialAnswers, errors}; } + + private ActionMessages validateQuestionAnswer(String newAnswer, Integer questionIndex, + GeneralLearnerFlowDTO generalLearnerFlowDTO) { + ActionMessages errors = new ActionMessages(); + + Map questionMap = generalLearnerFlowDTO.getMapQuestionContentLearner(); + QaQuestionDTO dto = questionMap.get(questionIndex); + // if so, check if the answer is blank and generate an error if it is blank. + boolean isRequiredQuestionMissed = dto.isRequired() && isEmpty(newAnswer); + if (isRequiredQuestionMissed) { + errors.add(Globals.ERROR_KEY, new ActionMessage("error.required", questionIndex)); + } + + boolean isMinWordsLimitReached = ValidationUtil.isMinWordsLimitReached(newAnswer, dto.getMinWordsLimit(), + Boolean.parseBoolean(generalLearnerFlowDTO.getAllowRichEditor())); + if (!isMinWordsLimitReached) { + errors.add(Globals.ERROR_KEY, new ActionMessage("label.minimum.number.words", ": " + dto.getMinWordsLimit())); + } + + return errors; + } + /** * Is this string empty? Need to strip out all HTML tags first otherwise an empty DIV might look like a valid answer * Smileys and math functions only put in an img tag so explicitly look for that.