Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java =================================================================== diff -u -rf0b909d0e482fe674e3c3d2f40ec7cc8d19b6803 -rbed43f31ad918bbd7f19f6db56fc90fcffcc37bd --- lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java (.../ExportToolContentService.java) (revision f0b909d0e482fe674e3c3d2f40ec7cc8d19b6803) +++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java (.../ExportToolContentService.java) (revision bed43f31ad918bbd7f19f6db56fc90fcffcc37bd) @@ -166,13 +166,13 @@ // export tool content zip file prefix public static final String EXPORT_TOOLCONTNET_ZIP_PREFIX = "lams_toolcontent_"; - public static final String EXPORT_LDCONTENT_ZIP_PREFIX = "lams_ldcontent_"; + public static final String EXPORT_CONTENT_ZIP_PREFIX = "lams_ldcontent_"; public static final String EXPORT_TOOLCONTNET_FOLDER_SUFFIX = "export_toolcontent"; public static final String EXPORT_TOOLCONTNET_ZIP_SUFFIX = ".zip"; - public static final String EXPORT_LDCONTENT_ZIP_SUFFIX = ".zip"; + public static final String EXPORT_CONTENT_ZIP_SUFFIX = ".zip"; public static final String LEARNING_DESIGN_FILE_NAME = "learning_design.xml"; @@ -425,6 +425,8 @@ // tools from LAMS repository. List activities = ldDto.getActivities(); + Set exportedContentFolders = new HashSet<>(); + // iterator all activities and export tool.xml and its // attachments for (AuthoringActivityDTO activity : activities) { @@ -470,6 +472,18 @@ writeErrorToToolFile(contentDir, activity.getToolContentID(), msg); toolsErrorMsgs.add(msg); } + + // export content folders for all QB questions which are used by LD's activities + List activityQbQuestions = qbDAO.getQuestionsByToolContentId(activity.getToolContentID()); + for (QbQuestion qbQuestion : activityQbQuestions) { + String contentFolderID = qbQuestion.getContentFolderId(); + // Do not export the same folder twice + // Some QB question share the same content folder ID + if (!exportedContentFolders.contains(contentFolderID)) { + exportContentFolder(contentFolderID, contentDir); + exportedContentFolders.add(contentFolderID); + } + } } // end all activities export // skipping unwanted elements; learning design DTO is altered @@ -497,27 +511,10 @@ log.debug("Learning design xml export success"); - try { - // create zip file for fckeditor unique content folder - String targetContentZipFileName = ExportToolContentService.EXPORT_LDCONTENT_ZIP_PREFIX - + ldDto.getContentFolderID() + ExportToolContentService.EXPORT_LDCONTENT_ZIP_SUFFIX; - String secureDir = Configuration.get(ConfigurationKeys.LAMS_EAR_DIR) + File.separator - + FileUtil.LAMS_WWW_DIR + File.separator + FileUtil.LAMS_WWW_SECURE_DIR; - - String ldContentDir = ExportToolContentService.getContentDirPath(ldDto.getContentFolderID(), true); - ldContentDir = FileUtil.getFullPath(secureDir, ldContentDir); - - if (!FileUtil.isEmptyDirectory(ldContentDir, true)) { - log.debug("Create export Learning Design content target zip file. File name is " - + targetContentZipFileName); - ZipFileUtil.createZipFile(targetContentZipFileName, ldContentDir, contentDir); - } else { - log.debug("No such directory (or empty directory):" + ldContentDir); - } - - } catch (Exception e) { - log.error("Error thrown while creating LD XML", e); - throw new ExportToolContentException(e); + // export the LDs content folder, if it was not already exported for one of the questions + String contentFolderID = ldDto.getContentFolderID(); + if (!exportedContentFolders.contains(contentFolderID)) { + exportContentFolder(contentFolderID, contentDir); } // zip file name with full path @@ -538,6 +535,30 @@ } } + private void exportContentFolder(String contentFolderID, String targetDir) throws ExportToolContentException { + try { + // create zip file for unique content folder + String zipFileName = ExportToolContentService.EXPORT_CONTENT_ZIP_PREFIX + contentFolderID + + ExportToolContentService.EXPORT_CONTENT_ZIP_SUFFIX; + String secureDir = Configuration.get(ConfigurationKeys.LAMS_EAR_DIR) + File.separator + + FileUtil.LAMS_WWW_DIR + File.separator + FileUtil.LAMS_WWW_SECURE_DIR; + + String contentFolder = ExportToolContentService.getContentDirPath(contentFolderID, true); + contentFolder = FileUtil.getFullPath(secureDir, contentFolder); + + if (!FileUtil.isEmptyDirectory(contentFolder, true)) { + log.debug("Create export content folder target zip file. File name is " + zipFileName); + ZipFileUtil.createZipFile(zipFileName, contentFolder, targetDir); + } else { + log.debug("No such directory (or empty directory):" + contentFolder); + } + + } catch (Exception e) { + log.error("Error thrown while a ZIP for content folder " + contentFolderID, e); + throw new ExportToolContentException(e); + } + } + /** * @throws ExportToolContentException * @@ -710,6 +731,7 @@ if (rewriteResourcePaths && ldDto.getDescription() != null) { ldDto.setDescription(ldDto.getDescription().replaceAll(oldResourcePath, newResourcePath)); } + Set importedContentFolder = new HashSet<>(); for (AuthoringActivityDTO activity : activities) { learningDesignService.fillLearningLibraryID(activity, activities); // skip non-tool activities @@ -775,12 +797,16 @@ fromVersion, toVersion); } + // import content folder for each QB question that LD's activities use List activityQbQuestions = qbDAO .getQuestionsByToolContentId(newContent.getToolContentId()); for (QbQuestion qbQuestion : activityQbQuestions) { - if (qbQuestion.getContentFolderId() == null) { - qbQuestion.setContentFolderId(ldDto.getContentFolderID()); - qbDAO.update(qbQuestion); + String contentFolderID = qbQuestion.getContentFolderId(); + // Do not import twice + // Some activities share the same content folder ID + if (!importedContentFolder.contains(contentFolderID)) { + importContentFolder(contentFolderID, learningDesignPath); + importedContentFolder.add(contentFolderID); } } @@ -802,23 +828,9 @@ return -1L; } - // begin ckeditor content folder import - try { - String contentZipFileName = ExportToolContentService.EXPORT_LDCONTENT_ZIP_PREFIX - + ldDto.getContentFolderID() + ExportToolContentService.EXPORT_LDCONTENT_ZIP_SUFFIX; - String secureDir = Configuration.get(ConfigurationKeys.LAMS_EAR_DIR) + File.separator - + FileUtil.LAMS_WWW_DIR + File.separator + FileUtil.LAMS_WWW_SECURE_DIR + File.separator - + ExportToolContentService.getContentDirPath(ldDto.getContentFolderID(), true); - File contentZipFile = new File(FileUtil.getFullPath(learningDesignPath, contentZipFileName)); - - // unzip file to target secure dir if exists - if (contentZipFile.exists()) { - InputStream is = new FileInputStream(contentZipFile); - ZipFileUtil.expandZipToFolder(is, secureDir); - } - - } catch (Exception e) { - throw new ImportToolContentException(e); + // import the LD's content folder, if it was not already imported for one of the QB questions + if (!importedContentFolder.contains(ldDto.getContentFolderID())) { + importContentFolder(ldDto.getContentFolderID(), learningDesignPath); } // if the design was read only (e.g. exported a runtime @@ -834,7 +846,26 @@ log.error("Exception occured during import.", e); throw new ImportToolContentException(e); } + } + private void importContentFolder(String contentFolderID, String sourceDir) throws ImportToolContentException { + try { + String contentZipFileName = ExportToolContentService.EXPORT_CONTENT_ZIP_PREFIX + contentFolderID + + ExportToolContentService.EXPORT_CONTENT_ZIP_SUFFIX; + String secureDir = Configuration.get(ConfigurationKeys.LAMS_EAR_DIR) + File.separator + + FileUtil.LAMS_WWW_DIR + File.separator + FileUtil.LAMS_WWW_SECURE_DIR + File.separator + + ExportToolContentService.getContentDirPath(contentFolderID, true); + File contentZipFile = new File(FileUtil.getFullPath(sourceDir, contentZipFileName)); + + // unzip file to target secure dir if exists + if (contentZipFile.exists()) { + InputStream is = new FileInputStream(contentZipFile); + ZipFileUtil.expandZipToFolder(is, secureDir); + } + + } catch (Exception e) { + throw new ImportToolContentException(e); + } } /** Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentImportContentVersionFilter.java =================================================================== diff -u -rf0b909d0e482fe674e3c3d2f40ec7cc8d19b6803 -rbed43f31ad918bbd7f19f6db56fc90fcffcc37bd --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentImportContentVersionFilter.java (.../AssessmentImportContentVersionFilter.java) (revision f0b909d0e482fe674e3c3d2f40ec7cc8d19b6803) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentImportContentVersionFilter.java (.../AssessmentImportContentVersionFilter.java) (revision bed43f31ad918bbd7f19f6db56fc90fcffcc37bd) @@ -22,11 +22,16 @@ package org.lamsfoundation.lams.tool.assessment.service; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + import org.lamsfoundation.lams.learningdesign.service.ToolContentVersionFilter; import org.lamsfoundation.lams.qb.QbUtils; import org.lamsfoundation.lams.tool.assessment.model.Assessment; @@ -112,6 +117,19 @@ * Migration to Question Bank */ public void up20190704To20190809(String toolFilePath) throws IOException { + // find LD's content folder ID to use it in new QB questions + String contentFolderId = null; + try { + File ldFile = new File(new File(toolFilePath).getParentFile().getParentFile(), "learning_design.xml"); + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = docBuilder.parse(new FileInputStream(ldFile)); + Element ldRoot = doc.getDocumentElement(); + contentFolderId = XMLUtil.getChildElementValue(ldRoot, "contentFolderID", null); + } catch (Exception e) { + throw new IOException("Error while extracting LD content folder ID for Question Bank migration", e); + } + final String contentFolderIdFinal = contentFolderId; + // tell which file to process and what to do with its root element transformXML(toolFilePath, toolRoot -> { Document document = toolRoot.getOwnerDocument(); @@ -137,6 +155,7 @@ XMLUtil.rewriteTextElement(assessmentQuestion, qbQuestion, "type", "type", null, false, true); // Question ID will be filled later as it requires QbService XMLUtil.addTextElement(qbQuestion, "version", "1"); + XMLUtil.addTextElement(qbQuestion, "contentFolderId", contentFolderIdFinal); XMLUtil.rewriteTextElement(toolRoot, qbQuestion, "created", "createDate", defaultCreateDate, true, false); XMLUtil.rewriteTextElement(assessmentQuestion, qbQuestion, "title", "name", null, false, true, Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java =================================================================== diff -u -r0730a2efec2b305e10b98b82c11e1ae2ef1b72fb -rbed43f31ad918bbd7f19f6db56fc90fcffcc37bd --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 0730a2efec2b305e10b98b82c11e1ae2ef1b72fb) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision bed43f31ad918bbd7f19f6db56fc90fcffcc37bd) @@ -2654,11 +2654,14 @@ for (AssessmentQuestion assessmentQuestion : toolContentObj.getQuestions()) { QbQuestion qbQuestion = assessmentQuestion.getQbQuestion(); + // try to match the question to an existing QB question in DB QbQuestion existingQuestion = qbService.getQuestionByUUID(qbQuestion.getUuid()); if (existingQuestion == null) { + // none found, create a new QB question qbService.insertQuestion(qbQuestion); qbService.addQuestionToCollection(publicQbCollectionUid, qbQuestion.getQuestionId(), false); } else { + // found, use the existing one assessmentQuestion.setQbQuestion(existingQuestion); } Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McImportContentVersionFilter.java =================================================================== diff -u -rf0b909d0e482fe674e3c3d2f40ec7cc8d19b6803 -rbed43f31ad918bbd7f19f6db56fc90fcffcc37bd --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McImportContentVersionFilter.java (.../McImportContentVersionFilter.java) (revision f0b909d0e482fe674e3c3d2f40ec7cc8d19b6803) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McImportContentVersionFilter.java (.../McImportContentVersionFilter.java) (revision bed43f31ad918bbd7f19f6db56fc90fcffcc37bd) @@ -1,9 +1,14 @@ package org.lamsfoundation.lams.tool.mc.service; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + import org.lamsfoundation.lams.learningdesign.service.ToolContentVersionFilter; import org.lamsfoundation.lams.qb.QbUtils; import org.lamsfoundation.lams.tool.mc.model.McContent; @@ -79,6 +84,19 @@ * Migration to Question Bank */ public void up20190517To20190809(String toolFilePath) throws IOException { + // find LD's content folder ID to use it in new QB questions + String contentFolderId = null; + try { + File ldFile = new File(new File(toolFilePath).getParentFile().getParentFile(), "learning_design.xml"); + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = docBuilder.parse(new FileInputStream(ldFile)); + Element ldRoot = doc.getDocumentElement(); + contentFolderId = XMLUtil.getChildElementValue(ldRoot, "contentFolderID", null); + } catch (Exception e) { + throw new IOException("Error while extracting LD content folder ID for Question Bank migration", e); + } + final String contentFolderIdFinal = contentFolderId; + // tell which file to process and what to do with its root element transformXML(toolFilePath, toolRoot -> { Document document = toolRoot.getOwnerDocument(); @@ -106,6 +124,7 @@ XMLUtil.addTextElement(qbQuestion, "type", "1"); // Question ID will be filled later as it requires QbService XMLUtil.addTextElement(qbQuestion, "version", "1"); + XMLUtil.addTextElement(qbQuestion, "contentFolderId", contentFolderIdFinal); XMLUtil.addTextElement(qbQuestion, "createDate", createDate); XMLUtil.rewriteTextElement(mcQuestion, qbQuestion, "mark", "maxMark", "1", false, true); XMLUtil.rewriteTextElement(mcQuestion, qbQuestion, "feedback", "feedback", null, false, true, Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java =================================================================== diff -u -r0730a2efec2b305e10b98b82c11e1ae2ef1b72fb -rbed43f31ad918bbd7f19f6db56fc90fcffcc37bd --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java (.../McService.java) (revision 0730a2efec2b305e10b98b82c11e1ae2ef1b72fb) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java (.../McService.java) (revision bed43f31ad918bbd7f19f6db56fc90fcffcc37bd) @@ -1433,11 +1433,14 @@ QbQuestion qbQuestion = mcQuestion.getQbQuestion(); qbQuestion.clearID(); + // try to match the question to an existing QB question in DB QbQuestion existingQuestion = qbService.getQuestionByUUID(qbQuestion.getUuid()); if (existingQuestion == null) { + // none found, create a new QB question qbService.insertQuestion(qbQuestion); qbService.addQuestionToCollection(publicQbCollectionUid, qbQuestion.getQuestionId(), false); } else { + // found, use the existing one mcQuestion.setQbQuestion(existingQuestion); } } Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieImportContentVersionFilter.java =================================================================== diff -u -rf0b909d0e482fe674e3c3d2f40ec7cc8d19b6803 -rbed43f31ad918bbd7f19f6db56fc90fcffcc37bd --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieImportContentVersionFilter.java (.../ScratchieImportContentVersionFilter.java) (revision f0b909d0e482fe674e3c3d2f40ec7cc8d19b6803) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieImportContentVersionFilter.java (.../ScratchieImportContentVersionFilter.java) (revision bed43f31ad918bbd7f19f6db56fc90fcffcc37bd) @@ -22,10 +22,15 @@ package org.lamsfoundation.lams.tool.scratchie.service; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + import org.lamsfoundation.lams.learningdesign.service.ToolContentVersionFilter; import org.lamsfoundation.lams.qb.QbUtils; import org.lamsfoundation.lams.tool.scratchie.dto.QbOptionDTO; @@ -94,11 +99,24 @@ this.removeField(ScratchieItem.class, "userMark"); this.removeField(ScratchieItem.class, "userAttempts"); } - + /** * Migration to Question Bank */ public void up20190103To20190809(String toolFilePath) throws IOException { + // find LD's content folder ID to use it in new QB questions + String contentFolderId = null; + try { + File ldFile = new File(new File(toolFilePath).getParentFile().getParentFile(), "learning_design.xml"); + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = docBuilder.parse(new FileInputStream(ldFile)); + Element ldRoot = doc.getDocumentElement(); + contentFolderId = XMLUtil.getChildElementValue(ldRoot, "contentFolderID", null); + } catch (Exception e) { + throw new IOException("Error while extracting LD content folder ID for Question Bank migration", e); + } + final String contentFolderIdFinal = contentFolderId; + // tell which file to process and what to do with its root element transformXML(toolFilePath, toolRoot -> { Document document = toolRoot.getOwnerDocument(); @@ -122,6 +140,7 @@ XMLUtil.addTextElement(qbQuestion, "type", "1"); // Question ID will be filled later as it requires QbService XMLUtil.addTextElement(qbQuestion, "version", "1"); + XMLUtil.addTextElement(qbQuestion, "contentFolderId", contentFolderIdFinal); XMLUtil.rewriteTextElement(scratchieQuestion, qbQuestion, "createDate", "createDate", new SimpleDateFormat(DateUtil.EXPORT_LD_FORMAT).format(new Date()), true, true); XMLUtil.rewriteTextElement(scratchieQuestion, qbQuestion, "title", "name", null, false, true, Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java =================================================================== diff -u -r0730a2efec2b305e10b98b82c11e1ae2ef1b72fb -rbed43f31ad918bbd7f19f6db56fc90fcffcc37bd --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java (.../ScratchieServiceImpl.java) (revision 0730a2efec2b305e10b98b82c11e1ae2ef1b72fb) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java (.../ScratchieServiceImpl.java) (revision bed43f31ad918bbd7f19f6db56fc90fcffcc37bd) @@ -1924,11 +1924,14 @@ QbQuestion qbQuestion = scratchieItem.getQbQuestion(); qbQuestion.clearID(); + // try to match the question to an existing QB question in DB QbQuestion existingQuestion = qbService.getQuestionByUUID(qbQuestion.getUuid()); if (existingQuestion == null) { + // none found, create a new QB question qbService.insertQuestion(qbQuestion); qbService.addQuestionToCollection(publicQbCollectionUid, qbQuestion.getQuestionId(), false); } else { + // found, use the existing one scratchieItem.setQbQuestion(existingQuestion); }