From 4f1fb25c36f7248ed2397966279737d61d980c38 Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Fri, 19 Jul 2019 18:16:25 +0200 Subject: [PATCH] OO-3890: import / export curriculum --- .../org/olat/core/util/vfs/LocalFileImpl.java | 6 +- .../org/olat/core/util/xml/XStreamHelper.java | 3 +- .../java/org/olat/course/CourseFactory.java | 4 +- .../olat/fileresource/types/FileResource.java | 4 +- .../manager/CurriculumImportHandler.java | 227 ++++++++++++ .../curriculum/manager/CurriculumXStream.java | 128 +++++++ .../ExportCurriculumMediaResource.java | 236 +++++++++++++ ...CurriculumElementToRepositoryEntryRef.java | 91 +++++ ...urriculumElementToRepositoryEntryRefs.java | 50 +++ .../CurriculumElementCalendarController.java | 3 +- .../ui/CurriculumListManagerController.java | 37 +- .../ui/ImportCurriculumController.java | 165 +++++++++ .../ui/_i18n/LocalStrings_de.properties | 2 + .../ui/_i18n/LocalStrings_en.properties | 2 + .../ui/RepositoryEntryRuntimeController.java | 4 +- .../ui/author/AuthorListController.java | 2 +- .../ImportRepositoryEntryController.java | 4 +- .../manager/CurriculumXStreamTest.java | 84 +++++ .../modules/curriculum/manager/curriculum.xml | 328 ++++++++++++++++++ .../curriculum/manager/curriculum_entries.xml | 95 +++++ 20 files changed, 1459 insertions(+), 16 deletions(-) create mode 100644 src/main/java/org/olat/modules/curriculum/manager/CurriculumImportHandler.java create mode 100644 src/main/java/org/olat/modules/curriculum/manager/CurriculumXStream.java create mode 100644 src/main/java/org/olat/modules/curriculum/manager/ExportCurriculumMediaResource.java create mode 100644 src/main/java/org/olat/modules/curriculum/model/CurriculumElementToRepositoryEntryRef.java create mode 100644 src/main/java/org/olat/modules/curriculum/model/CurriculumElementToRepositoryEntryRefs.java create mode 100644 src/main/java/org/olat/modules/curriculum/ui/ImportCurriculumController.java create mode 100644 src/test/java/org/olat/modules/curriculum/manager/CurriculumXStreamTest.java create mode 100644 src/test/java/org/olat/modules/curriculum/manager/curriculum.xml create mode 100644 src/test/java/org/olat/modules/curriculum/manager/curriculum_entries.xml diff --git a/src/main/java/org/olat/core/util/vfs/LocalFileImpl.java b/src/main/java/org/olat/core/util/vfs/LocalFileImpl.java index 768c7fc55b5..c4742d70ff2 100644 --- a/src/main/java/org/olat/core/util/vfs/LocalFileImpl.java +++ b/src/main/java/org/olat/core/util/vfs/LocalFileImpl.java @@ -168,10 +168,12 @@ public class LocalFileImpl extends LocalImpl implements VFSLeaf { private VFSStatus deleteBasefile() { VFSStatus status = VFSConstants.NO; try { - Files.delete(getBasefile().toPath()); + if(!Files.deleteIfExists(getBasefile().toPath())) { + log.debug("Cannot delete base file because it doesn't exist: {}", this); + } status = VFSConstants.YES; } catch(IOException e) { - log.error("Cannot delete base file: " + this, e); + log.error("Cannot delete base file: {}", this, e); } return status; } diff --git a/src/main/java/org/olat/core/util/xml/XStreamHelper.java b/src/main/java/org/olat/core/util/xml/XStreamHelper.java index 5efa6c9d26a..446bc767942 100644 --- a/src/main/java/org/olat/core/util/xml/XStreamHelper.java +++ b/src/main/java/org/olat/core/util/xml/XStreamHelper.java @@ -178,8 +178,7 @@ public class XStreamHelper { */ public static Object xstreamClone(Object in) { String data = unconfiguredXStream.toXML(in); - Object out = unconfiguredXStream.fromXML(data); - return out; + return unconfiguredXStream.fromXML(data); } /** diff --git a/src/main/java/org/olat/course/CourseFactory.java b/src/main/java/org/olat/course/CourseFactory.java index 2c749c57f57..5d9a1717725 100644 --- a/src/main/java/org/olat/course/CourseFactory.java +++ b/src/main/java/org/olat/course/CourseFactory.java @@ -552,11 +552,11 @@ public class CourseFactory { CourseConfigManager courseConfigMgr = CoreSpringFactory.getImpl(CourseConfigManager.class); courseConfigMgr.deleteConfigOf(newCourse); - // Unzip course strucure in new course + // Unzip course structure in new course LocalFolderImpl courseBaseContainer = newCourse.getCourseBaseContainer(); File fCanonicalCourseBasePath = courseBaseContainer.getBasefile(); if (ZipUtil.unzip(zipFile, fCanonicalCourseBasePath)) { - // Load course strucure now + // Load course structure now try { newCourse.load(); CourseConfig cc = courseConfigMgr.loadConfigFor(newCourse); diff --git a/src/main/java/org/olat/fileresource/types/FileResource.java b/src/main/java/org/olat/fileresource/types/FileResource.java index c7dd66add0f..dcd6535fb42 100644 --- a/src/main/java/org/olat/fileresource/types/FileResource.java +++ b/src/main/java/org/olat/fileresource/types/FileResource.java @@ -69,12 +69,12 @@ public class FileResource implements OLATResourceable { public FileResource() { typeName = GENERIC_TYPE_NAME; - typeId = new Long(CodeHelper.getForeverUniqueID()); + typeId = Long.valueOf(CodeHelper.getForeverUniqueID()); } public FileResource(String typeName) { this.typeName = typeName; - typeId = new Long(CodeHelper.getForeverUniqueID()); + typeId = Long.valueOf(CodeHelper.getForeverUniqueID()); } /** diff --git a/src/main/java/org/olat/modules/curriculum/manager/CurriculumImportHandler.java b/src/main/java/org/olat/modules/curriculum/manager/CurriculumImportHandler.java new file mode 100644 index 00000000000..dc26693615b --- /dev/null +++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumImportHandler.java @@ -0,0 +1,227 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.curriculum.manager; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; + +import org.apache.logging.log4j.Logger; +import org.olat.core.commons.persistence.DB; +import org.olat.core.id.Identity; +import org.olat.core.id.Organisation; +import org.olat.core.logging.Tracing; +import org.olat.core.util.PathUtils; +import org.olat.core.util.StringHelper; +import org.olat.core.util.WebappHelper; +import org.olat.course.CourseFactory; +import org.olat.course.ICourse; +import org.olat.fileresource.types.ResourceEvaluation; +import org.olat.modules.curriculum.Curriculum; +import org.olat.modules.curriculum.CurriculumElement; +import org.olat.modules.curriculum.CurriculumElementType; +import org.olat.modules.curriculum.CurriculumService; +import org.olat.modules.curriculum.model.CurriculumElementImpl; +import org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef; +import org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRefs; +import org.olat.modules.curriculum.model.CurriculumImpl; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryEntryStatusEnum; +import org.olat.repository.handlers.RepositoryHandler; +import org.olat.repository.handlers.RepositoryHandlerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * Initial date: 19 juil. 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +@Service +public class CurriculumImportHandler { + + private static final Logger log = Tracing.createLoggerFor(CurriculumImportHandler.class); + + @Autowired + private DB dbInstance; + @Autowired + private CurriculumService curriculumService; + @Autowired + private RepositoryHandlerFactory repositoryHandlerFactory; + + public String getCurriculumName(File archive) { + try (FileSystem fileSystem=FileSystems.newFileSystem(archive.toPath(), null)) { + Path curriculumXml = fileSystem.getPath("/curriculum.xml"); + Curriculum curriculum = CurriculumXStream.curriculumFromPath(curriculumXml); + if(curriculum != null) { + return curriculum.getDisplayName(); + } + } catch (Exception e) { + log.error("", e); + } + return null; + } + + public boolean importCurriculum(File archive, String curriculumName, Organisation organisation, Identity author, Locale locale) { + try (FileSystem fileSystem=FileSystems.newFileSystem(archive.toPath(), null)) { + Path curriculumXml = fileSystem.getPath("/curriculum.xml"); + Curriculum curriculum = CurriculumXStream.curriculumFromPath(curriculumXml); + if(curriculum == null) { + return false; + } + if(StringHelper.containsNonWhitespace(curriculumName)) { + curriculum.setDisplayName(curriculumName); + } + Map<Long,CurriculumElement> archiveKeyToCurriculumElements = new HashMap<>(); + importCurriculumStructure(curriculum, organisation, archiveKeyToCurriculumElements); + + Path curriculumEntriesXml = fileSystem.getPath("/curriculum_entries.xml"); + CurriculumElementToRepositoryEntryRefs entryRefs = CurriculumXStream.entryRefsFromPath(curriculumEntriesXml); + if(entryRefs != null) { + importEntries(entryRefs, archiveKeyToCurriculumElements, organisation, fileSystem, author, locale); + } + return true; + } catch (Exception e) { + log.error("", e); + return false; + } + } + + private void importEntries(CurriculumElementToRepositoryEntryRefs entryRefs, + Map<Long,CurriculumElement> archiveKeyToCurriculumElements, Organisation organisation, + FileSystem fileSystem, Identity author, Locale locale) throws IOException { + List<CurriculumElementToRepositoryEntryRef> entriesRefs = entryRefs.getEntryRefs(); + Map<Long,RepositoryEntry> archivedRepositoryEntryKeys = new HashMap<>(); + + for(CurriculumElementToRepositoryEntryRef entryRef:entriesRefs) { + CurriculumElement element = archiveKeyToCurriculumElements.get(entryRef.getCurriculumElementKey()); + if(element == null) { + continue; + } + + RepositoryEntry entry; + if(archivedRepositoryEntryKeys.containsKey(entryRef.getRepositoryEntryKey())) { + entry = archivedRepositoryEntryKeys.get(entryRef.getRepositoryEntryKey()); + } else { + entry = importRepositoryEntry(entryRef, organisation, fileSystem, author, locale); + } + if(entry != null) { + curriculumService.addRepositoryEntry(element, entry, false); + } + archivedRepositoryEntryKeys.put(entryRef.getRepositoryEntryKey(), entry); + } + } + + private RepositoryEntry importRepositoryEntry(CurriculumElementToRepositoryEntryRef archivedRef, Organisation organisation, + FileSystem fileSystem, Identity author, Locale locale) throws IOException { + String zipName = "repo_" + archivedRef.getRepositoryEntryKey() + ".zip"; + Path curriculumXml = fileSystem.getPath("/" + zipName); + + RepositoryEntry importedEntry = null; + if(Files.exists(curriculumXml)) { + File tmpArchive = new File(WebappHelper.getTmpDir(), UUID.randomUUID() + zipName); + tmpArchive.mkdirs(); + PathUtils.copyFileToDir(curriculumXml, tmpArchive.getParentFile(), tmpArchive.getName()); + + for(String type:repositoryHandlerFactory.getSupportedTypes()) { + RepositoryHandler handler = repositoryHandlerFactory.getRepositoryHandler(type); + ResourceEvaluation eval = handler.acceptImport(tmpArchive, tmpArchive.getName()); + if(eval != null && eval.isValid()) { + importedEntry = handler.importResource(author, archivedRef.getRepositoryEntryInitialAuthor(), + archivedRef.getRepositoryEntryDisplayname(), archivedRef.getRepositoryEntryDescription(), + true, organisation, locale, tmpArchive, zipName); + dbInstance.commit(); + if("CourseModule".equals(importedEntry.getOlatResource().getResourceableTypeName())) { + ICourse course = CourseFactory.loadCourse(importedEntry); + CourseFactory.publishCourse(course, RepositoryEntryStatusEnum.preparation, false, false, author, locale); + } + } + } + + if(!Files.deleteIfExists(tmpArchive.toPath())) { + log.warn("Cannot delete {}", tmpArchive.getAbsolutePath()); + } + dbInstance.commitAndCloseSession(); + } + return importedEntry; + } + + private void importCurriculumStructure(Curriculum archiveCurriculum, Organisation organisation, Map<Long,CurriculumElement> archiveKeyToCurriculumElements) { + Curriculum curriculum = curriculumService + .createCurriculum(archiveCurriculum.getIdentifier(), archiveCurriculum.getDisplayName(), archiveCurriculum.getDescription(), organisation); + curriculum.setDegree(archiveCurriculum.getDegree()); + curriculum.setStatus(archiveCurriculum.getStatus()); + curriculum = curriculumService.updateCurriculum(curriculum); + + List<CurriculumElementType> elementTypes = curriculumService.getCurriculumElementTypes(); + for(CurriculumElement rootElement:((CurriculumImpl)archiveCurriculum).getRootElements()) { + importCurriculumElements(curriculum, rootElement, null, elementTypes, archiveKeyToCurriculumElements); + } + } + + private void importCurriculumElements(Curriculum curriculum, CurriculumElement archivedElement, + CurriculumElement parentElement, List<CurriculumElementType> elementTypes, Map<Long,CurriculumElement> archiveKeyToCurriculumElements) { + if(archivedElement == null) return; + + CurriculumElementType elementType = findType(archivedElement.getType(), elementTypes); + + CurriculumElement element = curriculumService.createCurriculumElement(archivedElement.getIdentifier(), archivedElement.getDisplayName(), archivedElement.getElementStatus(), + archivedElement.getBeginDate(), archivedElement.getEndDate(), parentElement, elementType, + archivedElement.getCalendars(), archivedElement.getLectures(), curriculum); + element.setElementStatus(archivedElement.getElementStatus()); + + archiveKeyToCurriculumElements.put(archivedElement.getKey(), element); + + for(CurriculumElement childElement:((CurriculumElementImpl)archivedElement).getChildren()) { + importCurriculumElements(curriculum, childElement, element, elementTypes, archiveKeyToCurriculumElements); + } + } + + private CurriculumElementType findType(CurriculumElementType archivedType, List<CurriculumElementType> elementTypes) { + if(archivedType == null || elementTypes.isEmpty()) return null; + + for(CurriculumElementType elementType:elementTypes) { + if(elementType.getIdentifier() != null && elementType.getIdentifier().equals(archivedType.getIdentifier())) { + return elementType; + } + } + + for(CurriculumElementType elementType:elementTypes) { + if(elementType.getDisplayName() != null && elementType.getDisplayName().equals(archivedType.getDisplayName())) { + return elementType; + } + } + + CurriculumElementType newElementType = curriculumService.createCurriculumElementType(archivedType.getIdentifier(), + archivedType.getDisplayName(), archivedType.getDescription(), archivedType.getExternalId()); + elementTypes.add(newElementType); + dbInstance.commit(); + return newElementType; + } +} diff --git a/src/main/java/org/olat/modules/curriculum/manager/CurriculumXStream.java b/src/main/java/org/olat/modules/curriculum/manager/CurriculumXStream.java new file mode 100644 index 00000000000..30401379ad8 --- /dev/null +++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumXStream.java @@ -0,0 +1,128 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.curriculum.manager; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.zip.ZipOutputStream; + +import org.apache.logging.log4j.Logger; +import org.olat.core.logging.Tracing; +import org.olat.core.util.io.ShieldOutputStream; +import org.olat.core.util.xml.XStreamHelper; +import org.olat.modules.curriculum.Curriculum; +import org.olat.modules.curriculum.CurriculumCalendars; +import org.olat.modules.curriculum.CurriculumElement; +import org.olat.modules.curriculum.CurriculumElementToTaxonomyLevel; +import org.olat.modules.curriculum.CurriculumElementType; +import org.olat.modules.curriculum.CurriculumElementTypeManagedFlag; +import org.olat.modules.curriculum.CurriculumLectures; +import org.olat.modules.curriculum.model.CurriculumElementImpl; +import org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef; +import org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRefs; +import org.olat.modules.curriculum.model.CurriculumElementToTaxonomyLevelImpl; +import org.olat.modules.curriculum.model.CurriculumElementTypeImpl; +import org.olat.modules.curriculum.model.CurriculumImpl; +import org.olat.modules.portfolio.handler.BinderXStream; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.security.ExplicitTypePermission; + +/** + * + * Initial date: 17 juil. 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class CurriculumXStream { + + private static final Logger log = Tracing.createLoggerFor(BinderXStream.class); + private static final XStream xstream = XStreamHelper.createXStreamInstanceForDBObjects(); + + static { + XStream.setupDefaultSecurity(xstream); + Class<?>[] types = new Class[] { + Curriculum.class, CurriculumImpl.class, CurriculumElement.class, CurriculumElementImpl.class, + CurriculumElementType.class, CurriculumElementTypeImpl.class, + CurriculumElementTypeManagedFlag.class, CurriculumLectures.class, CurriculumCalendars.class, + CurriculumElementToTaxonomyLevel.class, CurriculumElementToTaxonomyLevelImpl.class, + CurriculumElementToRepositoryEntryRef.class, CurriculumElementToRepositoryEntryRefs.class, + Hashtable.class, HashMap.class + }; + xstream.addPermission(new ExplicitTypePermission(types)); + + xstream.omitField(CurriculumImpl.class, "group"); + xstream.omitField(CurriculumImpl.class, "organisation"); + xstream.omitField(CurriculumElementImpl.class, "group"); + xstream.omitField(CurriculumElementImpl.class, "curriculumParent"); + xstream.omitField(CurriculumElementImpl.class, "taxonomyLevels"); + } + + public static final Curriculum curriculumFromPath(Path path) + throws IOException { + try(InputStream inStream = Files.newInputStream(path)) { + return (Curriculum)xstream.fromXML(inStream); + } catch (Exception e) { + log.error("Cannot import this map: {}", path, e); + return null; + } + } + + public static final CurriculumElementToRepositoryEntryRefs entryRefsFromPath(Path path) + throws IOException { + try(InputStream inStream = Files.newInputStream(path)) { + return (CurriculumElementToRepositoryEntryRefs)xstream.fromXML(inStream); + } catch (Exception e) { + log.error("Cannot import this map: {}", path, e); + return null; + } + } + + public static final Curriculum fromXml(String xml) { + return (Curriculum)xstream.fromXML(xml); + } + + public static final String toXml(Curriculum curriculum) { + return xstream.toXML(curriculum); + } + + public static final void toStream(Curriculum curriculum, ZipOutputStream zout) + throws IOException { + try(OutputStream out=new ShieldOutputStream(zout)) { + xstream.toXML(curriculum, out); + } catch (Exception e) { + log.error("Cannot export this curriculum: {}", curriculum, e); + } + } + + public static final void toStream(CurriculumElementToRepositoryEntryRefs entryRefs, ZipOutputStream zout) + throws IOException { + try(OutputStream out=new ShieldOutputStream(zout)) { + xstream.toXML(entryRefs, out); + } catch (Exception e) { + log.error("Cannot export these entries references: {}", entryRefs, e); + } + } +} diff --git a/src/main/java/org/olat/modules/curriculum/manager/ExportCurriculumMediaResource.java b/src/main/java/org/olat/modules/curriculum/manager/ExportCurriculumMediaResource.java new file mode 100644 index 00000000000..403a7b8fccd --- /dev/null +++ b/src/main/java/org/olat/modules/curriculum/manager/ExportCurriculumMediaResource.java @@ -0,0 +1,236 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.curriculum.manager; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.logging.log4j.Logger; +import org.hibernate.Hibernate; +import org.olat.core.CoreSpringFactory; +import org.olat.core.gui.media.MediaResource; +import org.olat.core.id.OLATResourceable; +import org.olat.core.logging.Tracing; +import org.olat.core.util.FileUtils; +import org.olat.core.util.StringHelper; +import org.olat.core.util.io.HttpServletResponseOutputStream; +import org.olat.core.util.io.ShieldOutputStream; +import org.olat.modules.curriculum.Curriculum; +import org.olat.modules.curriculum.CurriculumElement; +import org.olat.modules.curriculum.CurriculumElementType; +import org.olat.modules.curriculum.CurriculumService; +import org.olat.modules.curriculum.model.CurriculumElementImpl; +import org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef; +import org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRefs; +import org.olat.modules.curriculum.model.CurriculumImpl; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.handlers.RepositoryHandler; +import org.olat.repository.handlers.RepositoryHandlerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 17 juil. 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class ExportCurriculumMediaResource implements MediaResource { + private static final Logger log = Tracing.createLoggerFor(ExportCurriculumMediaResource.class); + + private final Curriculum curriculum; + + @Autowired + private CurriculumService curriculumService; + @Autowired + private RepositoryHandlerFactory handlerFactory; + + public ExportCurriculumMediaResource(Curriculum curriculum) { + CoreSpringFactory.autowireObject(this); + this.curriculum = curriculum; + } + + @Override + public long getCacheControlDuration() { + return 0; + } + + @Override + public boolean acceptRanges() { + return false; + } + + @Override + public String getContentType() { + return "application/zip"; + } + + @Override + public Long getSize() { + return null; + } + + @Override + public InputStream getInputStream() { + return null; + } + + @Override + public Long getLastModified() { + return null; + } + + @Override + public void release() { + // + } + + @Override + public void prepare(HttpServletResponse hres) { + try { + hres.setCharacterEncoding("UTF-8"); + } catch (Exception e) { + log.error("", e); + } + + try(ZipOutputStream zout = new ZipOutputStream(hres.getOutputStream())) { + Curriculum loadedCurriculum = curriculumService.getCurriculum(curriculum); + unproxy(loadedCurriculum); + String label = loadedCurriculum.getDisplayName(); + String secureLabel = StringHelper.transformDisplayNameToFileSystemName(label); + + String file = secureLabel + ".zip"; + hres.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + StringHelper.urlEncodeUTF8(file)); + hres.setHeader("Content-Description", StringHelper.urlEncodeUTF8(label)); + + zout.setLevel(9); + + // curriculum structure + zout.putNextEntry(new ZipEntry("curriculum.xml")); + CurriculumXStream.toStream(loadedCurriculum, zout); + zout.closeEntry(); + + // curriculum element to repository entry + List<ExportRepositoryEntry> collectedEntries = new ArrayList<>(); + for(CurriculumElement element:((CurriculumImpl)loadedCurriculum).getRootElements()) { + collectEntries(element, collectedEntries); + } + CurriculumElementToRepositoryEntryRefs entryRefs = new CurriculumElementToRepositoryEntryRefs(new ArrayList<>()); + for(ExportRepositoryEntry collectedEntry:collectedEntries) { + entryRefs.getEntryRefs().add(new CurriculumElementToRepositoryEntryRef(collectedEntry.getEntry(), + collectedEntry.getCurriculumElement().getKey())); + } + zout.putNextEntry(new ZipEntry("curriculum_entries.xml")); + CurriculumXStream.toStream(entryRefs, zout); + zout.closeEntry(); + + // export repository entries + Set<Long> duplicates = new HashSet<>(); + for(ExportRepositoryEntry exportedEntry:collectedEntries) { + if(!duplicates.contains(exportedEntry.getEntry().getKey())) { + exportEntries(exportedEntry, zout); + duplicates.add(exportedEntry.getEntry().getKey()); + } + } + } catch (Exception e) { + log.error("", e); + } + } + + private void exportEntries(ExportRepositoryEntry exportedEntry, ZipOutputStream zout) + throws IOException { + RepositoryEntry entry = exportedEntry.getEntry(); + OLATResourceable ores = entry.getOlatResource(); + RepositoryHandler handler = handlerFactory.getRepositoryHandler(entry); + + MediaResource mr = handler.getAsMediaResource(ores); + zout.putNextEntry(new ZipEntry("repo_" + entry.getKey() + ".zip")); + + try(OutputStream out=new ShieldOutputStream(zout); + InputStream in = mr.getInputStream()) { + if(in == null) { + HttpServletResponseOutputStream response = new HttpServletResponseOutputStream(out); + mr.prepare(response); + } else { + FileUtils.copy(in, out); + } + } catch(Exception e) { + log.error("", e); + } + zout.closeEntry(); + } + + private void collectEntries(CurriculumElement curriculumElement, List<ExportRepositoryEntry> collectedEntries) { + if(curriculumElement == null) return; + + List<RepositoryEntry> entries = curriculumService.getRepositoryEntries(curriculumElement); + for(RepositoryEntry entry:entries) { + collectedEntries.add(new ExportRepositoryEntry(entry, curriculumElement)); + } + + for(CurriculumElement element:((CurriculumElementImpl)curriculumElement).getChildren()) { + collectEntries(element, collectedEntries); + } + } + + private void unproxy(Curriculum loadedCurriculum) { + loadedCurriculum.getOrganisation(); + for(CurriculumElement element:((CurriculumImpl)loadedCurriculum).getRootElements()) { + unproxy(element); + } + } + + private void unproxy(CurriculumElement curriculumElement) { + if(curriculumElement == null) return; + + curriculumElement.getCurriculum(); + curriculumElement.setType(Hibernate.unproxy(curriculumElement.getType(), CurriculumElementType.class)); + for(CurriculumElement element:((CurriculumElementImpl)curriculumElement).getChildren()) { + unproxy(element); + } + } + + private static class ExportRepositoryEntry { + + private final RepositoryEntry entry; + private final CurriculumElement curriculumElement; + + public ExportRepositoryEntry(RepositoryEntry entry, CurriculumElement curriculumElement) { + this.entry = entry; + this.curriculumElement = curriculumElement; + } + + public RepositoryEntry getEntry() { + return entry; + } + + public CurriculumElement getCurriculumElement() { + return curriculumElement; + } + } +} diff --git a/src/main/java/org/olat/modules/curriculum/model/CurriculumElementToRepositoryEntryRef.java b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementToRepositoryEntryRef.java new file mode 100644 index 00000000000..edcc28f6d31 --- /dev/null +++ b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementToRepositoryEntryRef.java @@ -0,0 +1,91 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.curriculum.model; + +import org.olat.repository.RepositoryEntry; + +/** + * This is only used for import/export operations + * + * Initial date: 19 juil. 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class CurriculumElementToRepositoryEntryRef { + + private Long repositoryEntryKey; + private String repositoryEntryDisplayname; + private String repositoryEntryDescription; + private String repositoryEntryInitialAuthor; + + private Long curriculumElementKey; + + public CurriculumElementToRepositoryEntryRef() { + // + } + + public CurriculumElementToRepositoryEntryRef(RepositoryEntry repositoryEntry, Long curriculumElementKey) { + repositoryEntryKey = repositoryEntry.getKey(); + repositoryEntryDisplayname = repositoryEntry.getDisplayname(); + repositoryEntryDescription = repositoryEntry.getDescription(); + repositoryEntryInitialAuthor = repositoryEntry.getInitialAuthor(); + this.curriculumElementKey = curriculumElementKey; + } + + public Long getRepositoryEntryKey() { + return repositoryEntryKey; + } + + public void setRepositoryEntryKey(Long repositoryEntryKey) { + this.repositoryEntryKey = repositoryEntryKey; + } + + public String getRepositoryEntryDisplayname() { + return repositoryEntryDisplayname; + } + + public void setRepositoryEntryDisplayname(String repositoryEntryDisplayname) { + this.repositoryEntryDisplayname = repositoryEntryDisplayname; + } + + public String getRepositoryEntryDescription() { + return repositoryEntryDescription; + } + + public void setRepositoryEntryDescription(String repositoryEntryDescription) { + this.repositoryEntryDescription = repositoryEntryDescription; + } + + public String getRepositoryEntryInitialAuthor() { + return repositoryEntryInitialAuthor; + } + + public void setRepositoryEntryInitialAuthor(String repositoryEntryInitialAuthor) { + this.repositoryEntryInitialAuthor = repositoryEntryInitialAuthor; + } + + public Long getCurriculumElementKey() { + return curriculumElementKey; + } + + public void setCurriculumElementKey(Long curriculumElementKey) { + this.curriculumElementKey = curriculumElementKey; + } +} diff --git a/src/main/java/org/olat/modules/curriculum/model/CurriculumElementToRepositoryEntryRefs.java b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementToRepositoryEntryRefs.java new file mode 100644 index 00000000000..472d35cbae7 --- /dev/null +++ b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementToRepositoryEntryRefs.java @@ -0,0 +1,50 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.curriculum.model; + +import java.util.List; + +/** + * This is only used for import/export operations + * + * Initial date: 19 juil. 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class CurriculumElementToRepositoryEntryRefs { + + private List<CurriculumElementToRepositoryEntryRef> entryRefs; + + public CurriculumElementToRepositoryEntryRefs() { + // + } + + public CurriculumElementToRepositoryEntryRefs(List<CurriculumElementToRepositoryEntryRef> entryRefs) { + this.entryRefs = entryRefs; + } + + public List<CurriculumElementToRepositoryEntryRef> getEntryRefs() { + return entryRefs; + } + + public void setEntryRefs(List<CurriculumElementToRepositoryEntryRef> entryRefs) { + this.entryRefs = entryRefs; + } +} diff --git a/src/main/java/org/olat/modules/curriculum/ui/CurriculumElementCalendarController.java b/src/main/java/org/olat/modules/curriculum/ui/CurriculumElementCalendarController.java index a19f90041c1..efb5d4f8a90 100644 --- a/src/main/java/org/olat/modules/curriculum/ui/CurriculumElementCalendarController.java +++ b/src/main/java/org/olat/modules/curriculum/ui/CurriculumElementCalendarController.java @@ -27,6 +27,7 @@ import java.util.List; import org.olat.commons.calendar.CalendarManager; import org.olat.commons.calendar.model.CalendarUserConfiguration; +import org.olat.commons.calendar.ui.CalendarController; import org.olat.commons.calendar.ui.WeeklyCalendarController; import org.olat.commons.calendar.ui.components.KalendarRenderWrapper; import org.olat.commons.calendar.ui.events.CalendarGUIModifiedEvent; @@ -84,7 +85,7 @@ public class CurriculumElementCalendarController extends BasicController impleme calendars = loadCalendars(ureq, entries); callerOres = OresHelper.createOLATResourceableInstance(CurriculumElement.class, element.getKey()); - calendarController = new WeeklyCalendarController(ureq, wControl, calendars, WeeklyCalendarController.CALLER_CURRICULUM, + calendarController = new WeeklyCalendarController(ureq, wControl, calendars, CalendarController.CALLER_CURRICULUM, callerOres, false); calendarController.setDifferentiateManagedEvent(CourseCalendars.needToDifferentiateManagedEvents(calendars)); listenTo(calendarController); diff --git a/src/main/java/org/olat/modules/curriculum/ui/CurriculumListManagerController.java b/src/main/java/org/olat/modules/curriculum/ui/CurriculumListManagerController.java index db23654fe26..7aec2e708bb 100644 --- a/src/main/java/org/olat/modules/curriculum/ui/CurriculumListManagerController.java +++ b/src/main/java/org/olat/modules/curriculum/ui/CurriculumListManagerController.java @@ -54,6 +54,7 @@ import org.olat.core.gui.control.controller.BasicController; import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowController; import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.gui.control.generic.dtabs.Activateable2; +import org.olat.core.gui.media.MediaResource; import org.olat.core.id.Roles; import org.olat.core.id.context.ContextEntry; import org.olat.core.id.context.StateEntry; @@ -65,6 +66,7 @@ import org.olat.modules.curriculum.CurriculumManagedFlag; import org.olat.modules.curriculum.CurriculumSecurityCallback; import org.olat.modules.curriculum.CurriculumSecurityCallbackFactory; import org.olat.modules.curriculum.CurriculumService; +import org.olat.modules.curriculum.manager.ExportCurriculumMediaResource; import org.olat.modules.curriculum.model.CurriculumElementRefImpl; import org.olat.modules.curriculum.model.CurriculumInfos; import org.olat.modules.curriculum.model.CurriculumSearchParameters; @@ -81,6 +83,7 @@ public class CurriculumListManagerController extends FormBasicController impleme private FlexiTableElement tableEl; private Link newCurriculumButton; + private Link importCurriculumButton; private CurriculumManagerDataModel tableModel; private final TooledStackedPanel toolbarPanel; @@ -88,6 +91,7 @@ public class CurriculumListManagerController extends FormBasicController impleme private CloseableModalController cmc; private CurriculumComposerController composerCtrl; private EditCurriculumController newCurriculumCtrl; + private ImportCurriculumController importCurriculumCtrl; private EditCurriculumOverviewController editCurriculumCtrl; private CloseableCalloutWindowController toolsCalloutCtrl; @@ -113,6 +117,10 @@ public class CurriculumListManagerController extends FormBasicController impleme @Override public void initTools() { if(secCallback.canNewCurriculum()) { + importCurriculumButton = LinkFactory.createToolLink("import.curriculum", translate("import.curriculum"), this, "o_icon_import"); + importCurriculumButton.setElementCssClass("o_sel_import_curriculum"); + toolbarPanel.addTool(importCurriculumButton, Align.left); + newCurriculumButton = LinkFactory.createToolLink("add.curriculum", translate("add.curriculum"), this, "o_icon_add"); newCurriculumButton.setElementCssClass("o_sel_add_curriculum"); toolbarPanel.addTool(newCurriculumButton, Align.left); @@ -246,7 +254,7 @@ public class CurriculumListManagerController extends FormBasicController impleme } cmc.deactivate(); cleanUp(); - } else if(editCurriculumCtrl == source) { + } else if(editCurriculumCtrl == source || importCurriculumCtrl == source) { if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { loadModel(tableEl.getQuickSearchString(), false); } @@ -259,8 +267,10 @@ public class CurriculumListManagerController extends FormBasicController impleme } private void cleanUp() { + removeAsListenerAndDispose(importCurriculumCtrl); removeAsListenerAndDispose(newCurriculumCtrl); removeAsListenerAndDispose(cmc); + importCurriculumCtrl = null; newCurriculumCtrl = null; cmc = null; } @@ -269,6 +279,8 @@ public class CurriculumListManagerController extends FormBasicController impleme public void event(UserRequest ureq, Component source, Event event) { if(newCurriculumButton == source) { doNewCurriculum(ureq); + } else if(importCurriculumButton == source) { + doImportCurriculum(ureq); } else if(toolbarPanel == source) { if(event instanceof PopEvent) { PopEvent pe = (PopEvent)event; @@ -311,6 +323,17 @@ public class CurriculumListManagerController extends FormBasicController impleme loadModel(event.getSearch(), true); } + private void doImportCurriculum(UserRequest ureq) { + if(importCurriculumCtrl != null) return; + + importCurriculumCtrl = new ImportCurriculumController(ureq, getWindowControl()); + listenTo(importCurriculumCtrl); + + cmc = new CloseableModalController(getWindowControl(), "close", importCurriculumCtrl.getInitialComponent(), true, translate("import.curriculum")); + listenTo(cmc); + cmc.activate(); + } + private void doNewCurriculum(UserRequest ureq) { if(newCurriculumCtrl != null) return; @@ -335,6 +358,12 @@ public class CurriculumListManagerController extends FormBasicController impleme } } + private void doExportCurriculum(UserRequest ureq, CurriculumRow row) { + Curriculum curriculum = curriculumService.getCurriculum(row); + MediaResource mr = new ExportCurriculumMediaResource(curriculum); + ureq.getDispatchResult().setResultingMediaResource(mr); + } + private void doSelectCurriculum(UserRequest ureq, CurriculumRow row, List<ContextEntry> entries) { Curriculum curriculum = curriculumService.getCurriculum(row); if(curriculum == null) { @@ -378,6 +407,7 @@ public class CurriculumListManagerController extends FormBasicController impleme private Link editLink; private Link deleteLink; + private Link exportLink; private final VelocityContainer mainVC; private CurriculumRow row; @@ -392,6 +422,8 @@ public class CurriculumListManagerController extends FormBasicController impleme //edit editLink = addLink("edit", "o_icon_edit", links); + exportLink = addLink("export", "o_icon_export", links); + if(!CurriculumManagedFlag.isManaged(curriculum, CurriculumManagedFlag.delete)) { links.add("-"); deleteLink = addLink("delete", "o_icon_delete_item", links); @@ -422,6 +454,9 @@ public class CurriculumListManagerController extends FormBasicController impleme } else if(deleteLink == source) { close(); showWarning("Not implemented"); + } else if(exportLink == source) { + close(); + doExportCurriculum(ureq, row); } } diff --git a/src/main/java/org/olat/modules/curriculum/ui/ImportCurriculumController.java b/src/main/java/org/olat/modules/curriculum/ui/ImportCurriculumController.java new file mode 100644 index 00000000000..d30e910c3d6 --- /dev/null +++ b/src/main/java/org/olat/modules/curriculum/ui/ImportCurriculumController.java @@ -0,0 +1,165 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.curriculum.ui; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.olat.basesecurity.OrganisationModule; +import org.olat.basesecurity.OrganisationRoles; +import org.olat.basesecurity.OrganisationService; +import org.olat.basesecurity.model.OrganisationRefImpl; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.FileElement; +import org.olat.core.gui.components.form.flexible.elements.SingleSelection; +import org.olat.core.gui.components.form.flexible.elements.TextElement; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.id.Organisation; +import org.olat.core.id.Roles; +import org.olat.core.util.StringHelper; +import org.olat.core.util.UserSession; +import org.olat.modules.curriculum.manager.CurriculumImportHandler; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 17 juil. 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class ImportCurriculumController extends FormBasicController { + + private FileElement uploadFileEl; + private TextElement displayNameEl; + private SingleSelection organisationEl; + + @Autowired + private OrganisationModule organisationModule; + @Autowired + private OrganisationService organisationService; + @Autowired + private CurriculumImportHandler curriculumImportHandler; + + public ImportCurriculumController(UserRequest ureq, WindowControl wControl) { + super(ureq, wControl); + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + uploadFileEl = uifactory.addFileElement(getWindowControl(), "curriculum.file", formLayout); + uploadFileEl.limitToMimeType(Collections.singleton("application/zip"), "error.mimetype", new String[]{ "ZIP" }); + uploadFileEl.addActionListener(FormEvent.ONCHANGE); + + displayNameEl = uifactory.addTextElement("curriculum.displayName", 255, "", formLayout); + + initFormOrganisations(formLayout, ureq.getUserSession()); + + FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); + formLayout.add(buttonsCont); + uifactory.addFormCancelButton("cancel", buttonsCont, ureq, getWindowControl()); + uifactory.addFormSubmitButton("import.curriculum", buttonsCont); + } + + private void initFormOrganisations(FormItemContainer formLayout, UserSession usess) { + Roles roles = usess.getRoles(); + List<Organisation> organisations = organisationService.getOrganisations(getIdentity(), roles, + OrganisationRoles.administrator, OrganisationRoles.curriculummanager); + + List<String> keyList = new ArrayList<>(); + List<String> valueList = new ArrayList<>(); + for(Organisation organisation:organisations) { + keyList.add(organisation.getKey().toString()); + valueList.add(organisation.getDisplayName()); + } + + organisationEl = uifactory.addDropdownSingleselect("curriculum.organisation", formLayout, + keyList.toArray(new String[keyList.size()]), valueList.toArray(new String[valueList.size()])); + organisationEl.setVisible(organisationModule.isEnabled()); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected boolean validateFormLogic(UserRequest ureq) { + boolean allOk = super.validateFormLogic(ureq); + + displayNameEl.clearError(); + if(!StringHelper.containsNonWhitespace(displayNameEl.getValue())) { + displayNameEl.setErrorKey("form.legende.mandatory", null); + allOk &= false; + } + + uploadFileEl.clearError(); + if(uploadFileEl.getUploadFile() == null) { + uploadFileEl.setErrorKey("form.legende.mandatory", null); + allOk &= false; + } else { + validateFormItem(uploadFileEl); + } + + return allOk; + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(uploadFileEl == source) { + if(!StringHelper.containsNonWhitespace(displayNameEl.getValue()) + && uploadFileEl.getUploadFile() != null) { + String name = curriculumImportHandler.getCurriculumName(uploadFileEl.getUploadFile()); + displayNameEl.setValue(name); + } + } + super.formInnerEvent(ureq, source, event); + } + + @Override + protected void formOK(UserRequest ureq) { + File archive = uploadFileEl.getUploadFile(); + + Organisation organisation; + if(organisationEl.isOneSelected()) { + Long organisationKey = Long.valueOf(organisationEl.getSelectedKey()); + organisation = organisationService.getOrganisation(new OrganisationRefImpl(organisationKey)); + } else { + organisation = organisationService.getDefaultOrganisation(); + } + String curriculumName = displayNameEl.getValue(); + curriculumImportHandler.importCurriculum(archive, curriculumName, organisation, getIdentity(), getLocale()); + fireEvent(ureq, Event.DONE_EVENT); + } + + @Override + protected void formCancelled(UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); + } +} diff --git a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties index 440d5cca7eb..ca81e116e4d 100644 --- a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties @@ -48,6 +48,7 @@ curriculum.external.id=Externe ID curriculum.identifier=Bezeichnung curriculum.in.my.courses.enabled=Curriculum in "Meine Kurse" curriculum.inactive.explain=Sie haben kein aktives Curriculum Element mit einem publizierten Kurs in diesem Curriculum. +curriculum.file=Datei curriculum.key=ID curriculum.metadata=Metadaten curriculum.organisation=Organisation @@ -61,6 +62,7 @@ error.target.no.insertion.point=Sie m\u00FCssen eine Position w\u00E4hlen. filter.active=$\:status.active filter.deleted=$\:status.deleted filter.inactive=$\:status.inactive +import.curriculum=Curriculum importieren import.member=$org.olat.group.ui.main\:import.member info.copy.element.type.sucessfull=Der Typ "{0}" wurde erfolgreich kopiert. override.member=Externe Verwaltung \u00FCbergehen diff --git a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties index d66b8c97e91..fdad3d3de55 100644 --- a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties @@ -48,6 +48,7 @@ curriculum.external.id=External ID curriculum.identifier=Identifier curriculum.in.my.courses.enabled=Curriculum in "My courses" curriculum.inactive.explain=You don't have an active curriculum element in this curriculum with a published course. +curriculum.file=File curriculum.key=ID curriculum.metadata=Metadata curriculum.organisation=Organisation @@ -61,6 +62,7 @@ error.target.no.insertion.point=You must choose a position. filter.active=$\:status.active filter.deleted=$\:status.deleted filter.inactive=$\:status.inactive +import.curriculum=Import curriculum import.member=$org.olat.group.ui.main\:import.member info.copy.element.type.sucessfull=The type "{0}" was successfully copied. lectures=Absences diff --git a/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java b/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java index 3ffa9de9215..640b2d205fa 100644 --- a/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java +++ b/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java @@ -643,7 +643,7 @@ public class RepositoryEntryRuntimeController extends MainLayoutBasicController } } - protected void processPopEvent(@SuppressWarnings("unused") UserRequest ureq, PopEvent pop) { + protected void processPopEvent(UserRequest ureq, PopEvent pop) { if(pop.getController() == settingsCtrl && settingsChanged) { RepositoryEntry entry = repositoryService.loadByKey(getRepositoryEntry().getKey()); refreshRepositoryEntry(entry); @@ -676,8 +676,6 @@ public class RepositoryEntryRuntimeController extends MainLayoutBasicController doClose(ureq); } else if(event instanceof ReloadSettingsEvent) { processReloadSettingsEvent((ReloadSettingsEvent)event); - } else if(event instanceof ReloadSettingsEvent) { - processReloadSettingsEvent((ReloadSettingsEvent)event); } else if (event == RepositoryEntryLifeCycleChangeController.closedEvent || event == RepositoryEntryLifeCycleChangeController.unclosedEvent) { processClosedUnclosedEvent(ureq); diff --git a/src/main/java/org/olat/repository/ui/author/AuthorListController.java b/src/main/java/org/olat/repository/ui/author/AuthorListController.java index 08a262428df..d749d7afef0 100644 --- a/src/main/java/org/olat/repository/ui/author/AuthorListController.java +++ b/src/main/java/org/olat/repository/ui/author/AuthorListController.java @@ -1064,7 +1064,7 @@ public class AuthorListController extends FormBasicController implements Activat boolean isAlreadyLocked = typeToDownload.isLocked(ores); try { lockResult = typeToDownload.acquireLock(ores, ureq.getIdentity()); - if(lockResult == null || (lockResult !=null && lockResult.isSuccess() && !isAlreadyLocked)) { + if(lockResult == null || (lockResult.isSuccess() && !isAlreadyLocked)) { MediaResource mr = typeToDownload.getAsMediaResource(ores); if(mr!=null) { repositoryService.incrementDownloadCounter(entry); diff --git a/src/main/java/org/olat/repository/ui/author/ImportRepositoryEntryController.java b/src/main/java/org/olat/repository/ui/author/ImportRepositoryEntryController.java index 1dc9f8761bd..ca624a1f8de 100644 --- a/src/main/java/org/olat/repository/ui/author/ImportRepositoryEntryController.java +++ b/src/main/java/org/olat/repository/ui/author/ImportRepositoryEntryController.java @@ -320,7 +320,7 @@ public class ImportRepositoryEntryController extends FormBasicController { String displayName = ""; boolean references = false; - if (handlers != null && handlers.size() > 0) { // add image and typename code + if (handlers != null && !handlers.isEmpty()) { // add image and typename code ResourceHandler handler = handlers.get(0); displayName = handler.getEval().getDisplayname(); references = handler.getEval().isReferences(); @@ -358,7 +358,7 @@ public class ImportRepositoryEntryController extends FormBasicController { if(references) { referencesEl.select(refKeys[0], true); } - importButton.setEnabled(handlers.size() > 0); + importButton.setEnabled(!handlers.isEmpty()); } private class ResourceHandler { diff --git a/src/test/java/org/olat/modules/curriculum/manager/CurriculumXStreamTest.java b/src/test/java/org/olat/modules/curriculum/manager/CurriculumXStreamTest.java new file mode 100644 index 00000000000..1720acbce94 --- /dev/null +++ b/src/test/java/org/olat/modules/curriculum/manager/CurriculumXStreamTest.java @@ -0,0 +1,84 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.curriculum.manager; + +import java.io.File; +import java.net.URL; +import java.util.Date; + +import org.junit.Assert; +import org.junit.Test; +import org.olat.core.commons.persistence.DB; +import org.olat.modules.curriculum.Curriculum; +import org.olat.modules.curriculum.CurriculumCalendars; +import org.olat.modules.curriculum.CurriculumElement; +import org.olat.modules.curriculum.CurriculumElementStatus; +import org.olat.modules.curriculum.CurriculumLectures; +import org.olat.modules.curriculum.CurriculumService; +import org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRefs; +import org.olat.test.OlatTestCase; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 17 juil. 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class CurriculumXStreamTest extends OlatTestCase { + + @Autowired + private DB dbInstance; + @Autowired + private CurriculumService curriculumService; + + @Test + public void toStream() { + Curriculum curriculum = curriculumService.createCurriculum("CUR-XSTREAM-1", "My Curriculum 1", "Short desc.", null); + CurriculumElement element = curriculumService.createCurriculumElement("Element-1", "1. Element", CurriculumElementStatus.active, + new Date(), new Date(), null, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum); + dbInstance.commitAndCloseSession(); + + Curriculum loadedCurriculum = curriculumService.getCurriculum(curriculum); + String xml = CurriculumXStream.toXml(loadedCurriculum); + Assert.assertTrue(xml.contains(curriculum.getDisplayName())); + Assert.assertTrue(xml.contains(element.getDisplayName())); + + Curriculum streamedCurriculum = CurriculumXStream.fromXml(xml); + Assert.assertNotNull(streamedCurriculum); + Assert.assertEquals(loadedCurriculum.getKey(), streamedCurriculum.getKey()); + } + + @Test + public void curriculumFromXml_version14x() throws Exception { + URL input = CurriculumXStreamTest.class.getResource("curriculum.xml"); + File inputFile = new File(input.getFile()); + Curriculum export = CurriculumXStream.curriculumFromPath(inputFile.toPath()); + Assert.assertNotNull(export); + } + + @Test + public void entriesFromXml_version14x() throws Exception { + URL input = CurriculumXStreamTest.class.getResource("curriculum_entries.xml"); + File inputFile = new File(input.getFile()); + CurriculumElementToRepositoryEntryRefs export = CurriculumXStream.entryRefsFromPath(inputFile.toPath()); + Assert.assertNotNull(export); + } +} diff --git a/src/test/java/org/olat/modules/curriculum/manager/curriculum.xml b/src/test/java/org/olat/modules/curriculum/manager/curriculum.xml new file mode 100644 index 00000000000..a747d3d67d3 --- /dev/null +++ b/src/test/java/org/olat/modules/curriculum/manager/curriculum.xml @@ -0,0 +1,328 @@ +<org.olat.modules.curriculum.model.CurriculumImpl> + <key>1</key> + <creationDate class="sql-timestamp">2018-07-13 14:04:18.723</creationDate> + <lastModified class="sql-timestamp">2018-07-13 14:04:18.723</lastModified> + <identifier>Physik</identifier> + <displayName>Physik</displayName> + <description></description> + <rootElements> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>1</key> + <creationDate class="sql-timestamp">2018-07-13 14:04:36.1</creationDate> + <lastModified class="sql-timestamp">2018-10-22 12:38:22.917</lastModified> + <posCurriculum>0</posCurriculum> + <identifier>Astrophysik</identifier> + <displayName>Astrophysik</displayName> + <description></description> + <calendarsEnabledString>disabled</calendarsEnabledString> + <status>active</status> + <materializedPathKeys>/1/</materializedPathKeys> + <children> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>8</key> + <creationDate class="sql-timestamp">2018-07-18 16:33:55.543</creationDate> + <lastModified class="sql-timestamp">2019-02-19 10:39:31.199</lastModified> + <pos>0</pos> + <identifier>Solar system</identifier> + <displayName>Solar system</displayName> + <description></description> + <calendarsEnabledString>disabled</calendarsEnabledString> + <lecturesEnabledString>enabled</lecturesEnabledString> + <status>active</status> + <beginDate class="sql-timestamp">2019-02-11 23:00:00</beginDate> + <endDate class="sql-timestamp">2019-02-27 23:00:00</endDate> + <materializedPathKeys>/1/8/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>10</key> + <creationDate class="sql-timestamp">2018-07-18 16:34:29.45</creationDate> + <lastModified class="sql-timestamp">2018-10-22 12:37:33.456</lastModified> + <pos>0</pos> + <identifier>Saturn</identifier> + <displayName>Saturn</displayName> + <description></description> + <calendarsEnabledString>enabled</calendarsEnabledString> + <status>active</status> + <materializedPathKeys>/1/8/10/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children/> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../../../.."/> + <type class="org.olat.modules.curriculum.model.CurriculumElementTypeImpl"> + <key>4</key> + <creationDate class="sql-timestamp">2018-10-22 12:35:23.921</creationDate> + <lastModified class="sql-timestamp">2018-10-22 12:35:23.923</lastModified> + <identifier>Seminar</identifier> + <displayName>Seminar</displayName> + <description></description> + <cssClass></cssClass> + <calendarsEnabledString>enabled</calendarsEnabledString> + <allowedSubTypes> + <isTempSession>false</isTempSession> + <initialized>false</initialized> + <owner class="org.olat.modules.curriculum.model.CurriculumElementTypeImpl" reference="../.."/> + <cachedSize>-1</cachedSize> + <role>org.olat.modules.curriculum.model.CurriculumElementTypeImpl.allowedSubTypes</role> + <key class="long">4</key> + <dirty>false</dirty> + <elementRemoved>false</elementRemoved> + <allowLoadOutsideTransaction>false</allowLoadOutsideTransaction> + </allowedSubTypes> + </type> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>11</key> + <creationDate class="sql-timestamp">2018-07-18 16:34:42.244</creationDate> + <lastModified class="sql-timestamp">2018-10-11 08:20:32.406</lastModified> + <pos>1</pos> + <identifier>Jupiter</identifier> + <displayName>Jupiter</displayName> + <description></description> + <calendarsEnabledString>disabled</calendarsEnabledString> + <status>active</status> + <beginDate class="sql-timestamp">2018-10-05 22:00:00</beginDate> + <endDate class="sql-timestamp">2018-10-26 22:00:00</endDate> + <materializedPathKeys>/1/8/11/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children/> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + </children> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../.."/> + <type class="org.olat.modules.curriculum.model.CurriculumElementTypeImpl"> + <key>2</key> + <creationDate class="sql-timestamp">2018-10-22 12:34:59.867</creationDate> + <lastModified class="sql-timestamp">2019-02-13 14:08:32.946</lastModified> + <identifier>Semester</identifier> + <displayName>Semester</displayName> + <description></description> + <cssClass></cssClass> + <calendarsEnabledString>enabled</calendarsEnabledString> + <lecturesEnabledString>enabled</lecturesEnabledString> + <allowedSubTypes> + <isTempSession>false</isTempSession> + <initialized>false</initialized> + <owner class="org.olat.modules.curriculum.model.CurriculumElementTypeImpl" reference="../.."/> + <cachedSize>-1</cachedSize> + <role>org.olat.modules.curriculum.model.CurriculumElementTypeImpl.allowedSubTypes</role> + <key class="long">2</key> + <dirty>false</dirty> + <elementRemoved>false</elementRemoved> + <allowLoadOutsideTransaction>false</allowLoadOutsideTransaction> + </allowedSubTypes> + </type> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>9</key> + <creationDate class="sql-timestamp">2018-07-18 16:34:14.697</creationDate> + <lastModified class="sql-timestamp">2018-10-15 14:27:47.814</lastModified> + <pos>1</pos> + <identifier>Galaxy</identifier> + <displayName>Galaxy</displayName> + <description></description> + <calendarsEnabledString>disabled</calendarsEnabledString> + <status>active</status> + <beginDate class="sql-timestamp">2018-09-30 22:00:00</beginDate> + <endDate class="sql-timestamp">2018-10-02 22:00:00</endDate> + <materializedPathKeys>/1/9/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children/> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + </children> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../.."/> + <type class="org.olat.modules.curriculum.model.CurriculumElementTypeImpl"> + <key>1</key> + <creationDate class="sql-timestamp">2018-10-05 07:18:24.409</creationDate> + <lastModified class="sql-timestamp">2018-10-22 12:35:04.245</lastModified> + <identifier>Bildungsgang</identifier> + <displayName>Bildungsgang</displayName> + <description></description> + <cssClass></cssClass> + <calendarsEnabledString>enabled</calendarsEnabledString> + <allowedSubTypes> + <isTempSession>false</isTempSession> + <initialized>false</initialized> + <owner class="org.olat.modules.curriculum.model.CurriculumElementTypeImpl" reference="../.."/> + <cachedSize>-1</cachedSize> + <role>org.olat.modules.curriculum.model.CurriculumElementTypeImpl.allowedSubTypes</role> + <key class="long">1</key> + <dirty>false</dirty> + <elementRemoved>false</elementRemoved> + <allowLoadOutsideTransaction>false</allowLoadOutsideTransaction> + </allowedSubTypes> + </type> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>2</key> + <creationDate class="sql-timestamp">2018-07-18 16:31:54.842</creationDate> + <lastModified class="sql-timestamp">2018-10-16 12:57:04.003</lastModified> + <posCurriculum>1</posCurriculum> + <identifier>Quantum Physik</identifier> + <displayName>Quantum Physik</displayName> + <description></description> + <calendarsEnabledString>enabled</calendarsEnabledString> + <status>active</status> + <materializedPathKeys>/2/</materializedPathKeys> + <children> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>25</key> + <creationDate class="sql-timestamp">2018-10-16 12:57:29.674</creationDate> + <lastModified class="sql-timestamp">2018-10-16 13:01:11.316</lastModified> + <pos>0</pos> + <identifier>Chromodynamique quantique</identifier> + <displayName>Chromodynamique quantique</displayName> + <description></description> + <calendarsEnabledString>enabled</calendarsEnabledString> + <status>active</status> + <materializedPathKeys>/2/25/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>27</key> + <creationDate class="sql-timestamp">2018-10-16 13:00:48.07</creationDate> + <lastModified class="sql-timestamp">2019-02-19 10:57:29.496</lastModified> + <pos>0</pos> + <identifier>Chromodynamique quantique aspects pratiques</identifier> + <displayName>Chromodynamique quantique aspects pratiques</displayName> + <description></description> + <calendarsEnabledString>disabled</calendarsEnabledString> + <lecturesEnabledString>enabled</lecturesEnabledString> + <status>active</status> + <materializedPathKeys>/2/25/27/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children/> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>28</key> + <creationDate class="sql-timestamp">2018-10-16 13:01:04.464</creationDate> + <lastModified class="sql-timestamp">2018-10-16 13:01:04.464</lastModified> + <pos>1</pos> + <identifier>Chromodynamique quantique théorie math.</identifier> + <displayName>Chromodynamique quantique théorie math.</displayName> + <calendarsEnabledString>disabled</calendarsEnabledString> + <status>active</status> + <materializedPathKeys>/2/25/28/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children/> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + </children> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>26</key> + <creationDate class="sql-timestamp">2018-10-16 12:58:20.489</creationDate> + <lastModified class="sql-timestamp">2018-10-16 12:58:20.489</lastModified> + <pos>1</pos> + <identifier>Mécanique quantique relativiste</identifier> + <displayName>Mécanique quantique relativiste</displayName> + <calendarsEnabledString>disabled</calendarsEnabledString> + <status>active</status> + <materializedPathKeys>/2/26/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children/> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + </children> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>3</key> + <creationDate class="sql-timestamp">2018-07-18 16:32:22.429</creationDate> + <lastModified class="sql-timestamp">2018-07-18 16:32:22.429</lastModified> + <posCurriculum>2</posCurriculum> + <identifier>Math</identifier> + <displayName>Math</displayName> + <status>active</status> + <materializedPathKeys>/3/</materializedPathKeys> + <children> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>4</key> + <creationDate class="sql-timestamp">2018-07-18 16:32:39.837</creationDate> + <lastModified class="sql-timestamp">2018-07-18 16:32:39.837</lastModified> + <pos>0</pos> + <identifier>Topology</identifier> + <displayName>Topology</displayName> + <status>active</status> + <materializedPathKeys>/3/4/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children/> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>5</key> + <creationDate class="sql-timestamp">2018-07-18 16:32:59.384</creationDate> + <lastModified class="sql-timestamp">2018-07-18 16:32:59.384</lastModified> + <pos>1</pos> + <identifier>Numerische Simulation</identifier> + <displayName>Numerische Simulation</displayName> + <status>inactive</status> + <materializedPathKeys>/3/5/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>6</key> + <creationDate class="sql-timestamp">2018-07-18 16:33:16.646</creationDate> + <lastModified class="sql-timestamp">2018-07-18 16:33:16.646</lastModified> + <pos>0</pos> + <identifier>R Software</identifier> + <displayName>R Software</displayName> + <status>active</status> + <materializedPathKeys>/3/5/6/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children/> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>7</key> + <creationDate class="sql-timestamp">2018-07-18 16:33:39.025</creationDate> + <lastModified class="sql-timestamp">2018-10-15 14:27:58.13</lastModified> + <pos>1</pos> + <identifier>Fortran</identifier> + <displayName>Fortran</displayName> + <description></description> + <calendarsEnabledString>disabled</calendarsEnabledString> + <status>active</status> + <beginDate class="sql-timestamp">2018-09-30 22:00:00</beginDate> + <endDate class="sql-timestamp">2018-10-02 22:00:00</endDate> + <materializedPathKeys>/3/5/7/</materializedPathKeys> + <parent class="org.olat.modules.curriculum.model.CurriculumElementImpl" reference="../../.."/> + <children/> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + </children> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + </children> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>17</key> + <creationDate class="sql-timestamp">2018-07-26 16:46:33.362</creationDate> + <lastModified class="sql-timestamp">2018-07-26 16:58:46.45</lastModified> + <pos>0</pos> + <posCurriculum>3</posCurriculum> + <identifier>To delete 5</identifier> + <displayName>To delete 5</displayName> + <status>deleted</status> + <materializedPathKeys>/17/</materializedPathKeys> + <children/> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + <org.olat.modules.curriculum.model.CurriculumElementImpl> + <key>20</key> + <creationDate class="sql-timestamp">2018-07-26 19:32:10.768</creationDate> + <lastModified class="sql-timestamp">2018-07-26 19:36:07.528</lastModified> + <pos>0</pos> + <posCurriculum>4</posCurriculum> + <identifier>To delete 8</identifier> + <displayName>To delete 8</displayName> + <status>deleted</status> + <materializedPathKeys>/20/</materializedPathKeys> + <children/> + <curriculum class="org.olat.modules.curriculum.model.CurriculumImpl" reference="../../.."/> + </org.olat.modules.curriculum.model.CurriculumElementImpl> + </rootElements> +</org.olat.modules.curriculum.model.CurriculumImpl> \ No newline at end of file diff --git a/src/test/java/org/olat/modules/curriculum/manager/curriculum_entries.xml b/src/test/java/org/olat/modules/curriculum/manager/curriculum_entries.xml new file mode 100644 index 00000000000..e6a05aff603 --- /dev/null +++ b/src/test/java/org/olat/modules/curriculum/manager/curriculum_entries.xml @@ -0,0 +1,95 @@ +<org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRefs> + <entryRefs> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>319913984</repositoryEntryKey> + <repositoryEntryDisplayname>4 Generate courses</repositoryEntryDisplayname> + <repositoryEntryDescription>Training<br /></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>1</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>1201504256</repositoryEntryKey> + <repositoryEntryDisplayname>Assessment docs</repositoryEntryDisplayname> + <repositoryEntryDescription></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>1</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>1408073728</repositoryEntryKey> + <repositoryEntryDisplayname>Curriculum course</repositoryEntryDisplayname> + <repositoryEntryDescription></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>1</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>1529020417</repositoryEntryKey> + <repositoryEntryDisplayname>Refactoring 3 (book)</repositoryEntryDisplayname> + <repositoryEntryDescription></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>1</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>321880064</repositoryEntryKey> + <repositoryEntryDisplayname>Course with members list</repositoryEntryDisplayname> + <repositoryEntryDescription></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>10</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>819789824</repositoryEntryKey> + <repositoryEntryDisplayname>Lectures</repositoryEntryDisplayname> + <repositoryEntryDescription><p>Bla bla</p></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>10</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>1495105536</repositoryEntryKey> + <repositoryEntryDisplayname>Curriculum calendar</repositoryEntryDisplayname> + <repositoryEntryDescription><p>This is a course in a curriculum with calender</p></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>10</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>1496940544</repositoryEntryKey> + <repositoryEntryDisplayname>Course curriculum Reï</repositoryEntryDisplayname> + <repositoryEntryDescription></repositoryEntryDescription> + <repositoryEntryInitialAuthor>rei</repositoryEntryInitialAuthor> + <curriculumElementKey>10</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>1603698688</repositoryEntryKey> + <repositoryEntryDisplayname>Optional Tasks</repositoryEntryDisplayname> + <repositoryEntryDescription></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>10</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>1495105536</repositoryEntryKey> + <repositoryEntryDisplayname>Curriculum calendar</repositoryEntryDisplayname> + <repositoryEntryDescription><p>This is a course in a curriculum with calender</p></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>27</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>1495105536</repositoryEntryKey> + <repositoryEntryDisplayname>Curriculum calendar</repositoryEntryDisplayname> + <repositoryEntryDescription><p>This is a course in a curriculum with calender</p></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>26</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>330956800</repositoryEntryKey> + <repositoryEntryDisplayname>Assessment mode</repositoryEntryDisplayname> + <repositoryEntryDescription></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>4</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + <repositoryEntryKey>977698816</repositoryEntryKey> + <repositoryEntryDisplayname>Participants folders</repositoryEntryDisplayname> + <repositoryEntryDescription></repositoryEntryDescription> + <repositoryEntryInitialAuthor>kanu</repositoryEntryInitialAuthor> + <curriculumElementKey>4</curriculumElementKey> + </org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRef> + </entryRefs> +</org.olat.modules.curriculum.model.CurriculumElementToRepositoryEntryRefs> \ No newline at end of file -- GitLab