Newer
Older
/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <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>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
*/
package org.olat.course;
import java.io.File;
import java.io.Serializable;
import org.olat.admin.quota.QuotaConstants;
import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.id.IdentityEnvironment;
import org.olat.core.id.OLATResourceable;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.OLATRuntimeException;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.FileUtils;
import org.olat.core.util.nodes.INode;
import org.olat.core.util.tree.TreeVisitor;
import org.olat.core.util.tree.Visitor;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSItem;
import org.olat.core.util.vfs.VFSLeaf;
import org.olat.core.util.vfs.callbacks.FullAccessWithLazyQuotaCallback;
import org.olat.core.util.vfs.callbacks.FullAccessWithQuotaCallback;
import org.olat.core.util.vfs.version.Versionable;
import org.olat.core.util.vfs.version.VersionsFileManager;
import org.olat.core.util.xml.XStreamHelper;
import org.olat.course.assessment.AssessmentHelper;
import org.olat.course.config.CourseConfig;
import org.olat.course.config.CourseConfigManager;
import org.olat.course.config.CourseConfigManagerImpl;
import org.olat.course.export.CourseEnvironmentMapper;
import org.olat.course.nodes.CourseNode.Processing;
import org.olat.course.run.environment.CourseEnvironment;
import org.olat.course.run.environment.CourseEnvironmentImpl;
import org.olat.course.tree.CourseEditorTreeModel;
import org.olat.course.tree.CourseEditorTreeNode;
import org.olat.modules.glossary.GlossaryManager;
import org.olat.modules.reminder.ReminderService;
import org.olat.modules.sharedfolder.SharedFolderManager;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryEntryImportExport;
import org.olat.repository.RepositoryManager;
import org.olat.resource.OLATResource;
/**
* Description:<br>
* Implementation of the course data structure. The course is defined using a
* runStructure and the editorTreeModel. Additional things are available through
* the courseEnvironment (e.g. access to managers (Factory methods) or the course
* configuration)<br/>
* It is allowed to save a course only if the course is in readAndWrite.
* <P>
* Initial Date: 12.08.2005 <br>
* @author Felix Jost
*/
public class PersistingCourseImpl implements ICourse, OLATResourceable, Serializable {
private static final long serialVersionUID = -1022498371474445868L;
public static String COURSE_ROOT_DIR_NAME = "course";
private static final String EDITORTREEMODEL_XML = "editortreemodel.xml";
private static final String RUNSTRUCTURE_XML = "runstructure.xml";
private static final String ORES_TYPE_NAME = CourseModule.getCourseTypeName();
private static final String COURSEFOLDER = "coursefolder";
private Long resourceableId;
private Structure runStructure;
private boolean hasAssessableNodes = false;
private CourseEditorTreeModel editorTreeModel;
private CourseConfig courseConfig;
private final CourseEnvironmentImpl courseEnvironment;
private OlatRootFolderImpl courseRootContainer;
private String courseTitle = null;
/** courseTitleSyncObj is a final Object only used for synchronizing the courseTitle getter - see OLAT-5654 */
private final Object courseTitleSyncObj = new Object();
private static OLog log = Tracing.createLoggerFor(PersistingCourseImpl.class);
//an PersistingCourseImpl instance could be readOnly if readAndWrite == false, or readAndWrite
private boolean readAndWrite = false; //default readOnly
public boolean isReadAndWrite() {
return readAndWrite;
}
public void setReadAndWrite(boolean readAndWrite) {
this.readAndWrite = readAndWrite;
}
/**
* Creates a new Course instance and creates the course filesystem if it does
* not already exist. Editor and run structures are not yet set. Use load() to
* initialize the editor and run structure from persisted XML structure.
*
* @param resource The OLAT resource
PersistingCourseImpl(OLATResource resource) {
this.resourceableId = resource.getResourceableId();
// prepare filesystem and set course base path and course folder paths
prepareFilesystem();
courseConfig = CourseConfigManagerImpl.getInstance().loadConfigFor(this); // load or init defaults
courseEnvironment = new CourseEnvironmentImpl(this, resource);
PersistingCourseImpl(RepositoryEntry courseEntry) {
courseTitle = courseEntry.getDisplayname();
resourceableId = courseEntry.getOlatResource().getResourceableId();
// prepare filesystem and set course base path and course folder paths
prepareFilesystem();
courseConfig = CourseConfigManagerImpl.getInstance().loadConfigFor(this); // load or init defaults
courseEnvironment = new CourseEnvironmentImpl(this, courseEntry);
}
/**
* @see org.olat.course.ICourse#getRunStructure()
*/
public Structure getRunStructure() {
return runStructure;
}
/**
* @see org.olat.course.ICourse#getEditorTreeModel()
*/
public CourseEditorTreeModel getEditorTreeModel() {
return editorTreeModel;
}
/**
* @see org.olat.course.ICourse#getCourseBasePath()
*/
public OlatRootFolderImpl getCourseBaseContainer() {
return courseRootContainer;
}

srosse
committed
@Override
public OlatRootFolderImpl getCourseExportDataDir() {
OlatRootFolderImpl vfsExportDir = (OlatRootFolderImpl)getCourseBaseContainer().resolve(ICourse.EXPORTED_DATA_FOLDERNAME);
if (vfsExportDir == null) {
vfsExportDir = getCourseBaseContainer().createChildContainer(ICourse.EXPORTED_DATA_FOLDERNAME);

srosse
committed
}
return vfsExportDir;
}
/**
* @see org.olat.course.ICourse#getCourseFolderPath()
*/
public VFSContainer getCourseFolderContainer() {
// add local course folder's children as read/write source and any sharedfolder as subfolder
MergedCourseContainer courseFolderContainer = new MergedCourseContainer(resourceableId, getCourseTitle());
courseFolderContainer.init(this);
@Override
public VFSContainer getCourseFolderContainer(boolean overrideReadOnly) {
// add local course folder's children as read/write source and any sharedfolder as subfolder
MergedCourseContainer courseFolderContainer = new MergedCourseContainer(resourceableId, getCourseTitle(), null, overrideReadOnly);
courseFolderContainer.init(this);
return courseFolderContainer;
}
@Override
public VFSContainer getCourseFolderContainer(IdentityEnvironment identityEnv) {
// add local course folder's children as read/write source and any sharedfolder as subfolder
MergedCourseContainer courseFolderContainer = new MergedCourseContainer(resourceableId, getCourseTitle(), identityEnv);
courseFolderContainer.init(this);
return courseFolderContainer;
}
/**
* @see org.olat.course.ICourse#getCourseEnvironment()
*/
public CourseEnvironment getCourseEnvironment() {
return courseEnvironment;
}
/**
* @see org.olat.course.ICourse#getCourseTitle()
*/
public String getCourseTitle() {
if (courseTitle == null) {
synchronized (courseTitleSyncObj) { //o_clusterOK by:ld/se
// load repository entry for this course and get title from it
courseTitle = RepositoryManager.getInstance().lookupDisplayNameByOLATResourceableId(resourceableId);
public void updateCourseEntry(RepositoryEntry courseEntry) {
courseTitle = courseEntry.getDisplayname();
courseEnvironment.updateCourseEntry(courseEntry);
}
/**
* Prepares the filesystem for this course.
*/
private void prepareFilesystem() {
// generate course base path
String relPath = File.separator + COURSE_ROOT_DIR_NAME + File.separator + getResourceableId().longValue();
courseRootContainer = new OlatRootFolderImpl(relPath, null);
File fBasePath = courseRootContainer.getBasefile();
if (!fBasePath.exists() && !fBasePath.mkdirs())
throw new OLATRuntimeException(this.getClass(), "Could not create course base path:" + courseRootContainer, null);
}
protected OlatRootFolderImpl getIsolatedCourseFolder() {
// create local course folder
OlatRootFolderImpl isolatedCourseFolder = new OlatRootFolderImpl(courseRootContainer.getRelPath() + File.separator + COURSEFOLDER, null);
// generate course folder
File fCourseFolder = isolatedCourseFolder.getBasefile();
if (!fCourseFolder.exists() && !fCourseFolder.mkdirs()) {
throw new OLATRuntimeException(this.getClass(),
"could not create course's coursefolder path:" + fCourseFolder.getAbsolutePath(), null);
}
FullAccessWithQuotaCallback secCallback = new FullAccessWithLazyQuotaCallback(isolatedCourseFolder.getRelPath(), QuotaConstants.IDENTIFIER_DEFAULT_COURSE);
isolatedCourseFolder.setLocalSecurityCallback(secCallback);
return isolatedCourseFolder;
}
protected File getIsolatedCourseBaseFolder() {
// create local course folder
OlatRootFolderImpl isolatedCourseFolder = new OlatRootFolderImpl(courseRootContainer.getRelPath() + File.separator + COURSEFOLDER, null);
return isolatedCourseFolder.getBasefile();
}
/**
* Save the run structure to disk, persist to the xml file
*/
void saveRunStructure() {
writeObject(RUNSTRUCTURE_XML, getRunStructure());
log.debug("saveRunStructure");
}
/**
* Save the editor tree model to disk, persist to the xml file
*/
void saveEditorTreeModel() {
writeObject(EDITORTREEMODEL_XML, getEditorTreeModel());
log.debug("saveEditorTreeModel");
}
/**
* @see org.olat.course.ICourse#exportToFilesystem(java.io.File)
* <p>
* See OLAT-5368: Course Export can take longer than say 2min.
* <p>
*/
@Override
public void exportToFilesystem(OLATResource originalCourseResource, File exportDirectory,
boolean runtimeDatas, boolean backwardsCompatible) {
long s = System.currentTimeMillis();
log.info("exportToFilesystem: exporting course "+this+" to "+exportDirectory+"...");
File fCourseBase = getCourseBaseContainer().getBasefile();
//make the folder structure
File fExportedDataDir = new File(exportDirectory, EXPORTED_DATA_FOLDERNAME);
fExportedDataDir.mkdirs();
//export course config
FileUtils.copyFileToDir(new File(fCourseBase, CourseConfigManager.COURSECONFIG_XML), exportDirectory, "course export courseconfig");
//export business groups
CourseEnvironmentMapper envMapper = getCourseEnvironment().getCourseGroupManager().getBusinessGroupEnvironment();
if(backwardsCompatible) {
//prevents duplicate names
envMapper.avoidDuplicateNames();
}
getCourseEnvironment().getCourseGroupManager().exportCourseBusinessGroups(fExportedDataDir, envMapper, runtimeDatas, backwardsCompatible);
if(backwardsCompatible) {
XStream xstream = CourseXStreamAliases.getReadCourseXStream();
Structure exportedStructure = (Structure)XStreamHelper.readObject(xstream, new File(fCourseBase, RUNSTRUCTURE_XML));
visit(new NodePostExportVisitor(envMapper, backwardsCompatible), exportedStructure.getRootNode());
XStreamHelper.writeObject(xstream, new File(exportDirectory, RUNSTRUCTURE_XML), exportedStructure);
CourseEditorTreeModel exportedEditorModel = (CourseEditorTreeModel)XStreamHelper.readObject(xstream, new File(fCourseBase, EDITORTREEMODEL_XML));
visit(new NodePostExportVisitor(envMapper, backwardsCompatible), exportedEditorModel.getRootNode());
XStreamHelper.writeObject(xstream, new File(exportDirectory, EDITORTREEMODEL_XML), exportedEditorModel);
} else {
// export editor structure
FileUtils.copyFileToDir(new File(fCourseBase, EDITORTREEMODEL_XML), exportDirectory, "course export exitortreemodel");
// export run structure
FileUtils.copyFileToDir(new File(fCourseBase, RUNSTRUCTURE_XML), exportDirectory, "course export runstructure");
}
// export layout and media folder
FileUtils.copyDirToDir(new File(fCourseBase, "layout"), exportDirectory, "course export layout folder");
FileUtils.copyDirToDir(new File(fCourseBase, "media"), exportDirectory, "course export media folder");
FileUtils.copyDirToDir(getIsolatedCourseBaseFolder(), exportDirectory, "course export folder");
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
// export any node data
log.info("exportToFilesystem: exporting course "+this+": exporting all nodes...");
Visitor visitor = new NodeExportVisitor(fExportedDataDir, this);
TreeVisitor tv = new TreeVisitor(visitor, getEditorTreeModel().getRootNode(), true);
tv.visitAll();
log.info("exportToFilesystem: exporting course "+this+": exporting all nodes...done.");
//OLAT-5368: do intermediate commit to avoid transaction timeout
// discussion intermediatecommit vs increased transaction timeout:
// pro intermediatecommit: not much
// pro increased transaction timeout: would fix OLAT-5368 but only move the problem
//@TODO OLAT-2597: real solution is a long-running background-task concept...
DBFactory.getInstance().intermediateCommit();
// export shared folder
CourseConfig config = getCourseConfig();
if (config.hasCustomSharedFolder()) {
log.info("exportToFilesystem: exporting course "+this+": shared folder...");
if (!SharedFolderManager.getInstance().exportSharedFolder(
config.getSharedFolderSoftkey(), fExportedDataDir)) {
// export failed, delete reference to shared folder in the course config
log.info("exportToFilesystem: exporting course "+this+": export of shared folder failed.");
config.setSharedFolderSoftkey(CourseConfig.VALUE_EMPTY_SHAREDFOLDER_SOFTKEY);
CourseConfigManagerImpl.getInstance().saveConfigTo(this, config);
}
log.info("exportToFilesystem: exporting course "+this+": shared folder...done.");
}
//OLAT-5368: do intermediate commit to avoid transaction timeout
// discussion intermediatecommit vs increased transaction timeout:
// pro intermediatecommit: not much
// pro increased transaction timeout: would fix OLAT-5368 but only move the problem
//@TODO OLAT-2597: real solution is a long-running background-task concept...
DBFactory.getInstance().intermediateCommit();
// export glossary
if (config.hasGlossary()) {
log.info("exportToFilesystem: exporting course "+this+": glossary...");
if (!GlossaryManager.getInstance().exportGlossary(
config.getGlossarySoftKey(), fExportedDataDir)) {
// export failed, delete reference to glossary in the course config
log.info("exportToFilesystem: exporting course "+this+": export of glossary failed.");
config.setGlossarySoftKey(null);
CourseConfigManagerImpl.getInstance().saveConfigTo(this, config);
}
log.info("exportToFilesystem: exporting course "+this+": glossary...done.");
}
//OLAT-5368: do intermediate commit to avoid transaction timeout
// discussion intermediatecommit vs increased transaction timeout:
// pro intermediatecommit: not much
// pro increased transaction timeout: would fix OLAT-5368 but only move the problem
//@TODO OLAT-2597: real solution is a long-running background-task concept...
DBFactory.getInstance().intermediateCommit();
log.info("exportToFilesystem: exporting course "+this+": configuration and repo data...");
// export configuration file
FileUtils.copyFileToDir(new File(fCourseBase, CourseConfigManager.COURSECONFIG_XML), exportDirectory, "course export configuration and repo info");
// export repo metadata
RepositoryManager rm = RepositoryManager.getInstance();
RepositoryEntry myRE = rm.lookupRepositoryEntry(this, true);
RepositoryEntryImportExport importExport = new RepositoryEntryImportExport(myRE, fExportedDataDir);
importExport.exportDoExportProperties();
//OLAT-5368: do intermediate commit to avoid transaction timeout
// discussion intermediatecommit vs increased transaction timeout:
// pro intermediatecommit: not much
// pro increased transaction timeout: would fix OLAT-5368 but only move the problem
//@TODO OLAT-2597: real solution is a long-running background-task concept...
DBFactory.getInstance().intermediateCommit();
//export reminders
CoreSpringFactory.getImpl(ReminderService.class)
.exportReminders(myRE, fExportedDataDir);
log.info("exportToFilesystem: exporting course "+this+" to "+exportDirectory+" done.");
log.info("finished export course '"+getCourseTitle()+"' in t="+Long.toString(System.currentTimeMillis()-s));
}
@Override

Dirk Furrer
committed
public void postCopy(CourseEnvironmentMapper envMapper, ICourse sourceCourse) {
Structure importedStructure = getRunStructure();

Dirk Furrer
committed
visit(new NodePostCopyVisitor(envMapper, Processing.runstructure, this, sourceCourse), importedStructure.getRootNode());
saveRunStructure();
CourseEditorTreeModel importedEditorModel = getEditorTreeModel();

Dirk Furrer
committed
visit(new NodePostCopyVisitor(envMapper, Processing.editor, this, sourceCourse), importedEditorModel.getRootNode());
saveEditorTreeModel();
}
@Override
public void postImport(File importDirectory, CourseEnvironmentMapper envMapper) {
Structure importedStructure = getRunStructure();
visit(new NodePostImportVisitor(importDirectory, this, envMapper, Processing.runstructure), importedStructure.getRootNode());
saveRunStructure();
CourseEditorTreeModel importedEditorModel = getEditorTreeModel();
visit(new NodePostImportVisitor(importDirectory, this, envMapper, Processing.editor), importedEditorModel.getRootNode());
saveEditorTreeModel();
}
private void visit(Visitor visitor, INode node) {
visitor.visit(node);
for(int i=node.getChildCount(); i-->0; ) {
INode subNode = node.getChildAt(i);
visit(visitor, subNode);
}
}
/**
* Load the course from disk/database, load the run structure from xml file etc.
*/
void load() {
/*
* remember that loading of the courseConfiguration is already done within
* the constructor !
*/
Object obj;
obj = readObject(RUNSTRUCTURE_XML);
if (!(obj instanceof Structure)) throw new AssertException("Error reading course run structure.");
runStructure = (Structure) obj;
initHasAssessableNodes();
obj = readObject(EDITORTREEMODEL_XML);
if (!(obj instanceof CourseEditorTreeModel)) throw new AssertException("Error reading course editor tree model.");
editorTreeModel = (CourseEditorTreeModel) obj;
}
/**
* Write a structure to an XML file in the course base path folder.
*
* @param fileName
* @param obj
*/
private void writeObject(String fileName, Object obj) {
VFSItem vfsItem = getCourseBaseContainer().resolve(fileName);
if (vfsItem == null) {
vfsItem = getCourseBaseContainer().createChildLeaf(fileName);
} else if(vfsItem.exists() && vfsItem instanceof Versionable) {
try {
VersionsFileManager.getInstance().addToRevisions((Versionable)vfsItem, null, "");
} catch (Exception e) {
log.error("Cannot versioned " + fileName, e);
}
XStream xstream = CourseXStreamAliases.getWriteCourseXStream();
XStreamHelper.writeObject(xstream, (VFSLeaf)vfsItem, obj);
}
/**
* Read a structure from XML file within the course base path folder.
*
* @param fileName
* @return de-serialized object
* @throws OLATRuntimeException if de-serialization fails.
*/
private Object readObject(String fileName) {
VFSItem vfsItem = getCourseBaseContainer().resolve(fileName);
if (vfsItem == null || !(vfsItem instanceof VFSLeaf)) {
throw new CorruptedCourseException("Cannot resolve file: " + fileName + " course=" + toString());
try {
XStream xstream = CourseXStreamAliases.getReadCourseXStream();
return XStreamHelper.readObject(xstream, ((VFSLeaf)vfsItem).getInputStream());
} catch (Exception e) {
log.error("Cannot read course tree file: " + fileName, e);
throw new CorruptedCourseException("Cannot resolve file: " + fileName + " course=" + toString(), e);
}
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
}
/**
* @see org.olat.core.id.OLATResourceablegetResourceableTypeName()
*/
public String getResourceableTypeName() {
return ORES_TYPE_NAME;
}
/**
* @see org.olat.core.id.OLATResourceablegetResourceableId()
*/
public Long getResourceableId() {
return resourceableId;
}
/**
* Package private. Only used by CourseFactory.
*
* @param editorTreeModel
*/
void setEditorTreeModel(CourseEditorTreeModel editorTreeModel) {
this.editorTreeModel = editorTreeModel;
}
/**
* Package private. Only used by CourseFactory.
*
* @param runStructure
*/
void setRunStructure(Structure runStructure) {
this.runStructure = runStructure;
initHasAssessableNodes();
}
/**
* This should only be called via the CourseFactory, since it has to update the course cache. <p>
* Sets the course configuration.
* @param courseConfig
*/
protected void setCourseConfig(CourseConfig courseConfig) {
this.courseConfig = courseConfig;
CourseConfigManagerImpl.getInstance().saveConfigTo(this, courseConfig);
}
/**
*
* @return
*/
public CourseConfig getCourseConfig() {
return courseConfig;
}
/**
* Sets information about there are assessable nodes or structure course nodes
* (subtype of assessable node), which 'hasPassedConfigured' or 'hasScoreConfigured'
* is true in the structure.
*/
void initHasAssessableNodes() {
this.hasAssessableNodes = AssessmentHelper.checkForAssessableNodes(runStructure.getRootNode());
}
/**
* @see org.olat.course.ICourse#hasAssessableNodes()
*/
public boolean hasAssessableNodes() {
return hasAssessableNodes;
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return "Course:[" + getResourceableId() + "," + courseTitle + "], " + super.toString();
}
}
class NodePostExportVisitor implements Visitor {
private final CourseEnvironmentMapper envMapper;
private final boolean backwardsCompatible;
public NodePostExportVisitor(CourseEnvironmentMapper envMapper, boolean backwardsCompatible) {
this.envMapper = envMapper;
this.backwardsCompatible = backwardsCompatible;
}
@Override
public void visit(INode node) {
if(node instanceof CourseEditorTreeNode) {
node = ((CourseEditorTreeNode)node).getCourseNode();
}
if(node instanceof CourseNode) {
((CourseNode)node).postExport(envMapper, backwardsCompatible);
}
}
}
class NodePostImportVisitor implements Visitor {
private final ICourse course;
private final File importDirectory;
private final Processing processType;
private final CourseEnvironmentMapper envMapper;
public NodePostImportVisitor(File importDirectory, ICourse course, CourseEnvironmentMapper envMapper, Processing processType) {
this.course = course;
this.envMapper = envMapper;
this.processType = processType;
this.importDirectory = importDirectory;
}
@Override
public void visit(INode node) {
if(node instanceof CourseEditorTreeNode) {
node = ((CourseEditorTreeNode)node).getCourseNode();
}
if(node instanceof CourseNode) {
((CourseNode)node).postImport(importDirectory, course, envMapper, processType);
}
}
}
class NodePostCopyVisitor implements Visitor {
private final Processing processType;
private final CourseEnvironmentMapper envMapper;

Dirk Furrer
committed
private final ICourse course;
private final ICourse sourceCourse;

Dirk Furrer
committed
public NodePostCopyVisitor(CourseEnvironmentMapper envMapper, Processing processType, ICourse course, ICourse sourceCourse) {
this.envMapper = envMapper;
this.processType = processType;

Dirk Furrer
committed
this.course = course;
this.sourceCourse = sourceCourse;
}
@Override
public void visit(INode node) {
if(node instanceof CourseEditorTreeNode) {
node = ((CourseEditorTreeNode)node).getCourseNode();
}
if(node instanceof CourseNode) {

Dirk Furrer
committed
((CourseNode)node).postCopy(envMapper, processType, course, sourceCourse);
}
}
}
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
class NodeExportVisitor implements Visitor {
private File exportDirectory;
private ICourse course;
/**
* Constructor of the node deletion visitor
*
* @param exportDirectory
* @param course
*/
public NodeExportVisitor(File exportDirectory, ICourse course) {
this.exportDirectory = exportDirectory;
this.course = course;
}
/**
* Visitor pattern to delete the course nodes
*
* @see org.olat.core.util.tree.Visitor#visit(org.olat.core.util.nodes.INode)
*/
public void visit(INode node) {
CourseEditorTreeNode cNode = (CourseEditorTreeNode) node;
cNode.getCourseNode().exportNode(exportDirectory, course);
//OLAT-5368: do frequent intermediate commits to avoid transaction timeout
// discussion intermediatecommit vs increased transaction timeout:
// pro intermediatecommit: not much
// pro increased transaction timeout: would fix OLAT-5368 but only move the problem
//@TODO OLAT-2597: real solution is a long-running background-task concept...
DBFactory.getInstance().intermediateCommit();
}
}