Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -recfb1a161014ebaff4f6988307878a11a54f310c -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision ecfb1a161014ebaff4f6988307878a11a54f310c) +++ lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -630,6 +630,7 @@ authoring.fla.liveedit.readonly.remove.parent.error = The activity can not be removed. Its parent activity is read-only. authoring.fla.liveedit.readonly.remove.child.error = The activity can not be removed. It has read-only child activities. authoring.fla.liveedit.readonly.remove.transition.error = The transition can not be removed. It is read-only. +authoring.fla.replace.question.prompt = The question you updated is present in other activities in this sequence. Do you want to update this question also in [0]? ckeditor.math.math = Math ckeditor.math.functions = Functions ckeditor.math.greek = Greek Index: lams_central/src/java/org/lamsfoundation/lams/web/qb/EditQbQuestionController.java =================================================================== diff -u -re423418cca5e77a3b54cb3be496b67abe01c4183 -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_central/src/java/org/lamsfoundation/lams/web/qb/EditQbQuestionController.java (.../EditQbQuestionController.java) (revision e423418cca5e77a3b54cb3be496b67abe01c4183) +++ lams_central/src/java/org/lamsfoundation/lams/web/qb/EditQbQuestionController.java (.../EditQbQuestionController.java) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -5,6 +5,7 @@ import java.lang.reflect.InvocationTargetException; import java.net.URLDecoder; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -27,6 +28,7 @@ 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.tool.ToolContent; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; @@ -44,6 +46,9 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.WebApplicationContext; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; + @Controller @RequestMapping("/qb/edit") public class EditQbQuestionController { @@ -395,6 +400,26 @@ return "qb/authoring/unitlist"; } + @RequestMapping(path = "/checkQuestionExistsInToolActivities") + @ResponseBody + public String checkQuestionExistsInToolActivities(@RequestParam(name = "toolContentIds[]") Set toolContentIds, + @RequestParam long qbQuestionUid, HttpServletResponse response) { + response.setContentType("application/json;charset=utf-8"); + ArrayNode responseJSON = JsonNodeFactory.instance.arrayNode(); + Collection toolContents = qbService.getQuestionActivities(qbQuestionUid, toolContentIds); + for (ToolContent toolContent : toolContents) { + responseJSON.add(toolContent.getToolContentId()); + } + return responseJSON.toString(); + } + + @RequestMapping(path = "/replaceQuestionInToolActivities", method = RequestMethod.POST) + @ResponseBody + public void replaceQuestionInToolActivities(@RequestParam(name = "toolContentIds[]") Set toolContentIds, + @RequestParam long oldQbQuestionUid, @RequestParam long newQbQuestionUid) { + qbService.replaceQuestionInToolActivities(toolContentIds, oldQbQuestionUid, newQbQuestionUid); + } + /** * Get answer options from HttpRequest * Index: lams_central/web/authoring/authoring.jsp =================================================================== diff -u -r09831591952e208b90ca0df0bdaa98d670017e04 -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_central/web/authoring/authoring.jsp (.../authoring.jsp) (revision 09831591952e208b90ca0df0bdaa98d670017e04) +++ lams_central/web/authoring/authoring.jsp (.../authoring.jsp) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -104,6 +104,8 @@ IMPORT_PART_CHOOSE_PROMPT : decoderDiv.html('').text(), LIVEEDIT_CANCEL_CONFIRM : '', + + REPLACE_QUESTION_PROMPT : decoderDiv.html('').text(), FOLDER_NOT_SELECTED_ERROR : decoderDiv.html('').text(), Index: lams_central/web/includes/javascript/authoring/authoringGeneral.js =================================================================== diff -u -r955d3cf3aaf3ffc011e780393e684b43748eaf8e -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_central/web/includes/javascript/authoring/authoringGeneral.js (.../authoringGeneral.js) (revision 955d3cf3aaf3ffc011e780393e684b43748eaf8e) +++ lams_central/web/includes/javascript/authoring/authoringGeneral.js (.../authoringGeneral.js) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -1661,6 +1661,39 @@ }, /** + * Check if given question exists in tool activities in this sequence + */ + checkQuestionExistsInToolActivities : function(qbQuestionUid) { + let resultToolContentIds = [], + candidateToolContentIds = []; + + // list all tool activities + $.each(layout.activities, function() { + if (this.toolContentID) { + candidateToolContentIds.push(this.toolContentID); + } + }); + + // ask back end if given activities contain the given question + $.ajax({ + cache : false, + async : false, + url : LAMS_URL + "qb/edit/checkQuestionExistsInToolActivities.do", + dataType : 'json', + data : { + 'toolContentIds' : candidateToolContentIds, + 'qbQuestionUid' : qbQuestionUid + }, + success : function(responseToolContentIds){ + // the response is list of tool content IDs which contain the given question + resultToolContentIds = responseToolContentIds; + } + }); + + return resultToolContentIds; + }, + + /** * Escapes HTML tags to prevent XSS injection. */ escapeHtml : function(unsafe) { @@ -2795,7 +2828,43 @@ }; }, + /** + * Replaces the old question with new question in given tool activities in this sequence + */ + replaceQuestionInToolActivities : function(thisToolContentId, allToolContentIds, oldQbQuestionUid, newQbQuestionUid) { + let activityTitles = '', + questionReplaced = false; + + // list names of all tool activities which contain the old question + $.each(layout.activities, function() { + if (this.toolContentID && this.toolContentID != thisToolContentId && allToolContentIds.indexOf(this.toolContentID) > -1) { + activityTitles += '"' + this.title + '", '; + } + }); + // ask teacher if he wants to update other activities too + if (activityTitles != '' && confirm(LABELS.REPLACE_QUESTION_PROMPT.replace('[0]', activityTitles.substring(0, activityTitles.length - 2)))) { + $.ajax({ + type : 'POST', + cache : false, + async : false, + url : LAMS_URL + "qb/edit/replaceQuestionInToolActivities.do", + dataType : 'text', + data : { + 'toolContentIds' : allToolContentIds, + 'oldQbQuestionUid' : oldQbQuestionUid, + 'newQbQuestionUid' : newQbQuestionUid + }, + success : function(){ + questionReplaced = true; + } + }); + } + + return questionReplaced; + }, + + resizeImportPartFrame : function(svgHeight) { $('#ldStoreDialogImportPartFrame').height(svgHeight + 40); }, Index: lams_common/src/java/org/lamsfoundation/lams/qb/dao/IQbDAO.java =================================================================== diff -u -rc393d2b92ec8c8bdc5916ac5f53e0adcaeee0ce3 -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_common/src/java/org/lamsfoundation/lams/qb/dao/IQbDAO.java (.../IQbDAO.java) (revision c393d2b92ec8c8bdc5916ac5f53e0adcaeee0ce3) +++ lams_common/src/java/org/lamsfoundation/lams/qb/dao/IQbDAO.java (.../IQbDAO.java) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -10,6 +10,7 @@ import org.lamsfoundation.lams.learningdesign.ToolActivity; import org.lamsfoundation.lams.qb.model.QbCollection; import org.lamsfoundation.lams.qb.model.QbQuestion; +import org.lamsfoundation.lams.tool.ToolContent; public interface IQbDAO extends IBaseDAO { @@ -37,6 +38,8 @@ List getQuestionActivities(long qbQuestionUid); + List getQuestionActivities(long qbQuestionUid, Collection filteringToolContentIds); + int getCountQuestionActivitiesByUid(long qbQuestionUid); int getCountQuestionActivitiesByQuestionId(int qbQuestionId); Index: lams_common/src/java/org/lamsfoundation/lams/qb/dao/hibernate/QbDAO.java =================================================================== diff -u -rc393d2b92ec8c8bdc5916ac5f53e0adcaeee0ce3 -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_common/src/java/org/lamsfoundation/lams/qb/dao/hibernate/QbDAO.java (.../QbDAO.java) (revision c393d2b92ec8c8bdc5916ac5f53e0adcaeee0ce3) +++ lams_common/src/java/org/lamsfoundation/lams/qb/dao/hibernate/QbDAO.java (.../QbDAO.java) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -19,6 +19,7 @@ import org.lamsfoundation.lams.qb.dao.IQbDAO; import org.lamsfoundation.lams.qb.model.QbCollection; import org.lamsfoundation.lams.qb.model.QbQuestion; +import org.lamsfoundation.lams.tool.ToolContent; public class QbDAO extends LAMSBaseDAO implements IQbDAO { @@ -32,6 +33,9 @@ + "WHERE a.toolContentId = q.toolContentId AND l IS NOT EMPTY AND l.lessonStateId IN (3,4,5,6) AND q.qbQuestion.uid = :qbQuestionUid " + "ORDER BY l.organisation.name, l.lessonName"; + private static final String FIND_QUESTION_ACTIVITIES_FILTERED_BY_TOOL_CONTENT_ID = "SELECT c FROM QbToolQuestion AS q, ToolContent AS c " + + "WHERE c.toolContentId = q.toolContentId AND q.qbQuestion.uid = :qbQuestionUid AND q.toolContentId IN (:toolContentIds)"; + private static final String FIND_QUESTION_VERSIONS = "SELECT q FROM QbQuestion AS q, QbQuestion AS r " + "WHERE q.questionId = r.questionId AND q.uid <> r.uid AND r.uid = :qbQuestionUid"; @@ -196,6 +200,14 @@ @Override @SuppressWarnings("unchecked") + public List getQuestionActivities(long qbQuestionUid, Collection filteringToolContentIds) { + return this.getSession().createQuery(FIND_QUESTION_ACTIVITIES_FILTERED_BY_TOOL_CONTENT_ID) + .setParameter("qbQuestionUid", qbQuestionUid) + .setParameterList("toolContentIds", filteringToolContentIds).list(); + } + + @Override + @SuppressWarnings("unchecked") public List getQuestionVersions(long qbQuestionUid) { return this.getSession().createQuery(FIND_QUESTION_VERSIONS).setParameter("qbQuestionUid", qbQuestionUid) .list(); Index: lams_common/src/java/org/lamsfoundation/lams/qb/service/IQbService.java =================================================================== diff -u -rc393d2b92ec8c8bdc5916ac5f53e0adcaeee0ce3 -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_common/src/java/org/lamsfoundation/lams/qb/service/IQbService.java (.../IQbService.java) (revision c393d2b92ec8c8bdc5916ac5f53e0adcaeee0ce3) +++ lams_common/src/java/org/lamsfoundation/lams/qb/service/IQbService.java (.../IQbService.java) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -11,6 +11,7 @@ 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.tool.ToolContent; import org.lamsfoundation.lams.usermanagement.Organisation; public interface IQbService { @@ -150,4 +151,8 @@ int mergeQuestions(long sourceQbQuestionUid, long targetQbQuestionUid); boolean isQuestionDefaultInTool(long qbQuestionUid, String toolSignature); + + Collection getQuestionActivities(long qbQuestionUid, Collection toolContentIds); + + void replaceQuestionInToolActivities(Collection toolContentIds, long oldQbQuestionUid, long newQbQuestionUid); } Index: lams_common/src/java/org/lamsfoundation/lams/qb/service/QbService.java =================================================================== diff -u -r7faeaea7b800b429b1bce93191090f0851e8f9f5 -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_common/src/java/org/lamsfoundation/lams/qb/service/QbService.java (.../QbService.java) (revision 7faeaea7b800b429b1bce93191090f0851e8f9f5) +++ lams_common/src/java/org/lamsfoundation/lams/qb/service/QbService.java (.../QbService.java) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -37,8 +37,10 @@ import org.lamsfoundation.lams.qb.model.QbQuestion; import org.lamsfoundation.lams.qb.model.QbQuestionUnit; import org.lamsfoundation.lams.qb.model.QbToolQuestion; +import org.lamsfoundation.lams.tool.ToolContent; import org.lamsfoundation.lams.tool.service.ILamsCoreToolService; import org.lamsfoundation.lams.tool.service.ILamsToolService; +import org.lamsfoundation.lams.tool.service.IQbToolService; import org.lamsfoundation.lams.usermanagement.Organisation; import org.lamsfoundation.lams.usermanagement.Role; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; @@ -735,6 +737,28 @@ return qbQuestions.stream().anyMatch(q -> q.getUid().equals(qbQuestionUid)); } + @Override + public Collection getQuestionActivities(long qbQuestionUid, Collection toolContentIds) { + return qbDAO.getQuestionActivities(qbQuestionUid, toolContentIds); + } + + @Override + public void replaceQuestionInToolActivities(Collection toolContentIds, long oldQbQuestionUid, + long newQbQuestionUid) { + for (Long toolContentId : toolContentIds) { + ToolContent toolContent = qbDAO.findByProperty(ToolContent.class, "toolContentId", toolContentId).get(0); + Object toolService = lamsCoreToolService.findToolService(toolContent.getTool()); + if (toolService instanceof IQbToolService) { + try { + ((IQbToolService) toolService).replaceQuestion(toolContentId, oldQbQuestionUid, newQbQuestionUid); + } catch (UnsupportedOperationException e) { + log.warn("Could not replace a question for activity with tool content ID " + toolContentId + + " as the tool does not support question replacement"); + } + } + } + } + private static Integer getUserId() { HttpSession ss = SessionManager.getSession(); UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER); Index: lams_common/src/java/org/lamsfoundation/lams/tool/service/IQbToolService.java =================================================================== diff -u -rf9377e7d8d81e048c6218184f598826038b0cb35 -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_common/src/java/org/lamsfoundation/lams/tool/service/IQbToolService.java (.../IQbToolService.java) (revision f9377e7d8d81e048c6218184f598826038b0cb35) +++ lams_common/src/java/org/lamsfoundation/lams/tool/service/IQbToolService.java (.../IQbToolService.java) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -17,4 +17,9 @@ * Replaces existing questions in an activity with ones provided as a parameter. */ List replaceQuestions(long toolContentId, String newActivityName, List newQuestions); + + /** + * Replaces existing question in an activity with one provided as a parameter. + */ + void replaceQuestion(long toolContentId, long oldQbQuestionUid, long newQbQuestionUid); } \ No newline at end of file Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java =================================================================== diff -u -r36716004da333fcc5eb3f776801e3e5278719fd7 -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 36716004da333fcc5eb3f776801e3e5278719fd7) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -2767,6 +2767,21 @@ return result; } + + @Override + public void replaceQuestion(long toolContentId, long oldQbQuestionUid, long newQbQuestionUid) { + Assessment assessment = getAssessmentByContentId(toolContentId); + QbQuestion newQbQuestion = null; + for (AssessmentQuestion assessmentQuestion : assessment.getQuestions()) { + if (assessmentQuestion.getQbQuestion().getUid().equals(oldQbQuestionUid)) { + if (newQbQuestion == null) { + newQbQuestion = qbService.getQuestionByUid(newQbQuestionUid); + } + assessmentQuestion.setQbQuestion(newQbQuestion); + } + } + } + // ***************************************************************************** // private methods // ***************************************************************************** Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java =================================================================== diff -u -rf9377e7d8d81e048c6218184f598826038b0cb35 -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java (.../McService.java) (revision f9377e7d8d81e048c6218184f598826038b0cb35) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java (.../McService.java) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -2234,6 +2234,11 @@ } @Override + public void replaceQuestion(long toolContentId, long oldQbQuestionUid, long newQbQuestionUid) { + throw new UnsupportedOperationException("MCQ tool does not support single question replacement yet"); + } + + @Override public void setConfigValue(String key, String value) { mcConfigDAO.setConfigValue(key, value); } Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java =================================================================== diff -u -ra05bb8ff92c659cc340b037de664fd0d9b103c96 -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java (.../ScratchieServiceImpl.java) (revision a05bb8ff92c659cc340b037de664fd0d9b103c96) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java (.../ScratchieServiceImpl.java) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -2221,6 +2221,20 @@ return result; } + @Override + public void replaceQuestion(long toolContentId, long oldQbQuestionUid, long newQbQuestionUid) { + Scratchie scratchie = getScratchieByContentId(toolContentId); + QbQuestion newQbQuestion = null; + for (ScratchieItem item : scratchie.getScratchieItems()) { + if (item.getQbQuestion().getUid().equals(oldQbQuestionUid)) { + if (newQbQuestion == null) { + newQbQuestion = qbService.getQuestionByUid(newQbQuestionUid); + } + item.setQbQuestion(newQbQuestion); + } + } + } + // ***************************************************************************** // set methods for Spring Bean // ***************************************************************************** Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/AuthoringController.java =================================================================== diff -u -r0f6e04d06144f4d55986abbf6fbcf2222cf0d43c -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/AuthoringController.java (.../AuthoringController.java) (revision 0f6e04d06144f4d55986abbf6fbcf2222cf0d43c) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/AuthoringController.java (.../AuthoringController.java) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -554,7 +554,12 @@ } userManagementService.save(updatedQuestion); item.setQbQuestion(updatedQuestion); + request.setAttribute("qbQuestionModified", isQbQuestionModified); + if (isQbQuestionModified == IQbService.QUESTION_MODIFIED_VERSION_BUMP) { + request.setAttribute("oldQbQuestionUid", qbQuestion.getUid()); + request.setAttribute("newQbQuestionUid", updatedQuestion.getUid()); + } //take care about question's collections. add to collection first Long oldCollectionUid = form.getOldCollectionUid(); Index: lams_tool_scratchie/web/pages/authoring/parts/itemlist.jsp =================================================================== diff -u -r73db6f5aae9d7fdaf209abcf1967d3fd96e6954f -rc82d171dbdd8918839e396f2851d6e11b68bb9e8 --- lams_tool_scratchie/web/pages/authoring/parts/itemlist.jsp (.../itemlist.jsp) (revision 73db6f5aae9d7fdaf209abcf1967d3fd96e6954f) +++ lams_tool_scratchie/web/pages/authoring/parts/itemlist.jsp (.../itemlist.jsp) (revision c82d171dbdd8918839e396f2851d6e11b68bb9e8) @@ -11,9 +11,25 @@ switch (qbQuestionModified) { case <%= IQbService.QUESTION_MODIFIED_UPDATE %>: qbMessage = ''; - break; case <%= IQbService.QUESTION_MODIFIED_VERSION_BUMP %>: - qbMessage = ''; + let showMessage = true; + + // check if we are in main authoring environment + if (typeof window.parent.GeneralLib != 'undefined') { + // check if any other activities require updating + let activitiesWithQuestion = window.parent.GeneralLib.checkQuestionExistsInToolActivities('${oldQbQuestionUid}'); + if (activitiesWithQuestion.length > 1) { + showMessage = false; + // update, if teacher agrees to it + window.parent.GeneralLib.replaceQuestionInToolActivities('${sessionMap.toolContentID}', activitiesWithQuestion, + '${oldQbQuestionUid}','${newQbQuestionUid}'); + } + } + + if (showMessage) { + qbMessage = ''; + } + break; case <%= IQbService.QUESTION_MODIFIED_ID_BUMP %>: qbMessage = '';