diff --git a/src/main/java/org/olat/course/CourseFactory.java b/src/main/java/org/olat/course/CourseFactory.java index 223898330698a40c45259d3b7cee89f270381386..f24d4b0d80705de683e336f424a4527869cbe2b1 100644 --- a/src/main/java/org/olat/course/CourseFactory.java +++ b/src/main/java/org/olat/course/CourseFactory.java @@ -573,6 +573,10 @@ public class CourseFactory extends BasicManager { FileUtils.deleteDirsAndFiles(fCanonicalCourseBasePath, true, true); return null; } + + private void upgrade() { + + } /** * Deploys a course from an exported course ZIP file. This process is unatended and diff --git a/src/main/java/org/olat/course/CourseUpgrade.java b/src/main/java/org/olat/course/CourseUpgrade.java index 087c56e7203c9ebc6a6dfbb2220981658939bd96..0f9fb990f0410350d39440b717b2d9035dba86c8 100644 --- a/src/main/java/org/olat/course/CourseUpgrade.java +++ b/src/main/java/org/olat/course/CourseUpgrade.java @@ -19,22 +19,40 @@ */ package org.olat.course; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; -import org.olat.core.gui.components.tree.TreeNode; -import org.olat.core.logging.LogDelegator; -import org.olat.core.util.Formatter; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.nodes.INode; -import org.olat.core.util.tree.TreeVisitor; -import org.olat.core.util.tree.Visitor; +import org.olat.course.condition.Condition; +import org.olat.course.nodes.AbstractAccessableCourseNode; +import org.olat.course.nodes.AbstractFeedCourseNode; +import org.olat.course.nodes.BCCourseNode; +import org.olat.course.nodes.COCourseNode; +import org.olat.course.nodes.CalCourseNode; import org.olat.course.nodes.CourseNode; -import org.olat.course.nodes.MSCourseNode; +import org.olat.course.nodes.DialogCourseNode; +import org.olat.course.nodes.ENCourseNode; +import org.olat.course.nodes.FOCourseNode; +import org.olat.course.nodes.GenericCourseNode; +import org.olat.course.nodes.InfoCourseNode; +import org.olat.course.nodes.PortfolioCourseNode; +import org.olat.course.nodes.ProjectBrokerCourseNode; +import org.olat.course.nodes.STCourseNode; import org.olat.course.nodes.TACourseNode; +import org.olat.course.nodes.WikiCourseNode; +import org.olat.course.nodes.co.COEditController; +import org.olat.course.run.scoring.ScoreCalculator; import org.olat.course.tree.CourseEditorTreeModel; +import org.olat.course.tree.CourseEditorTreeNode; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupService; +import org.olat.group.area.BGArea; +import org.olat.group.area.BGAreaManager; import org.olat.modules.ModuleConfiguration; +import org.olat.resource.OLATResource; /** * @@ -46,31 +64,401 @@ import org.olat.modules.ModuleConfiguration; * Initial Date: 17.07.2009 <br> * @author Roman Haag, www.frentix.com, roman.haag@frentix.com, */ -public class CourseUpgrade extends LogDelegator { - private static final String MS_TYPE = "ms"; +public class CourseUpgrade { + + private static final OLog log = Tracing.createLoggerFor(CourseUpgrade.class); + + private BGAreaManager areaManager; + private BusinessGroupService businessGroupService; public CourseUpgrade(){ // } - public void migrateCourse(ICourse course){ - PersistingCourseImpl ccourse = (PersistingCourseImpl) course; - // only upgrade from version 1 => 2 - // this will migrate wiki-syntax to html - int migrateTargetVersion = 2; - CourseEditorTreeModel editorTreeModel = course.getEditorTreeModel(); - if (!editorTreeModel.isVersionUpToDate() && editorTreeModel.getVersion() != migrateTargetVersion){ - logError("as of OpenOLAT 8, old courses with verison 1 are no longer supported. No migration done! Upgrade to 7.0 first!", null); - } - Structure runStructure = course.getRunStructure(); - if (!runStructure.isVersionUpToDate() && runStructure.getVersion() != migrateTargetVersion){ - logError("as of OpenOLAT 8, old courses with verison 1 are no longer supported. No migration done! Upgrade to 7.0 first!", null); + /** + * [used by Spring] + * @param areaManager + */ + public void setAreaManager(BGAreaManager areaManager) { + this.areaManager = areaManager; + } + + /** + * [used by Spring] + * @param businessGroupService + */ + public void setBusinessGroupService(BusinessGroupService businessGroupService) { + this.businessGroupService = businessGroupService; + } + + /** + * Upgrade the version of the course to 3 + * @param courseResource + * @param delegateOpeningEditSession + */ + public void processCourse(OLATResource courseResource, boolean delegateOpeningEditSession) { + ICourse opendedCourse = null; + try { + List<BusinessGroup> groups = businessGroupService.findBusinessGroups(null, courseResource, 0, -1); + List<BGArea> areas = areaManager.findBGAreasInContext(courseResource); + + ICourse course = CourseFactory.loadCourse(courseResource); + ProcessingOccured processingFlag = new ProcessingOccured(); + log.info("Start: " + course.getCourseTitle()); + Structure structure = course.getRunStructure(); + if(structure.getVersion() < 3) { + processCourseRec(processingFlag, structure.getRootNode(), groups, areas); + structure.setVersion(3); + processingFlag.processed(); + } + CourseEditorTreeModel editorTree = course.getEditorTreeModel(); + if(editorTree.getVersion() < 3) { + processEditorCourseRec(processingFlag, editorTree.getRootNode(), groups, areas); + editorTree.setVersion(3); + processingFlag.processed(); + } + + if(processingFlag.isProcessed()) { + if(delegateOpeningEditSession) { + opendedCourse = CourseFactory.openCourseEditSession(course.getResourceableId()); + } + + log.info("Save of:" + course.getCourseTitle() + " (" + course.getResourceableId() + ")"); + CourseFactory.saveCourse(course.getResourceableId()); + } else { + log.info("No change for: " + course.getCourseTitle() + " (" + course.getResourceableId() + ")"); + } + } catch (Exception e) { + log.error("", e); + } finally { + if(opendedCourse != null && delegateOpeningEditSession) { + CourseFactory.closeCourseEditSession(opendedCourse.getResourceableId(), true); + } } } + private void processCourseRec(ProcessingOccured processingFlag, INode node, + List<BusinessGroup> groups, List<BGArea> areas) { + if(node instanceof CourseNode) { + processCourseNode(processingFlag, (CourseNode)node, groups, areas); + } + for(int i=node.getChildCount(); i-->0; ) { + INode subNode = node.getChildAt(i); + processCourseRec(processingFlag, subNode, groups, areas); + } + } + private void processEditorCourseRec(ProcessingOccured processingFlag, INode node, + List<BusinessGroup> groups, List<BGArea> areas) { + if(node instanceof CourseEditorTreeNode) { + processCourseNode(processingFlag, ((CourseEditorTreeNode)node).getCourseNode(), groups, areas); + } + for(int i=node.getChildCount(); i-->0; ) { + INode subNode = node.getChildAt(i); + processEditorCourseRec(processingFlag, subNode, groups, areas); + } + } + /** + * Update all the conditions in every type of course node + * @param processingFlag + * @param node + * @param groups + * @param areas + */ + private void processCourseNode(ProcessingOccured processingFlag, CourseNode node, + List<BusinessGroup> groups, List<BGArea> areas) { + if(node instanceof GenericCourseNode) { + GenericCourseNode genericNode = (GenericCourseNode)node; + processCondition(processingFlag, genericNode.getPreConditionAccess(), groups, areas); + processCondition(processingFlag, genericNode.getPreConditionVisibility(), groups, areas); + if(node instanceof AbstractAccessableCourseNode) { + //nothing self but + + if(node instanceof WikiCourseNode) { + WikiCourseNode wikiNode = (WikiCourseNode)node; + processCondition(processingFlag, wikiNode.getPreConditionEdit(), groups, areas); + } else if(node instanceof STCourseNode) { + STCourseNode structureNode = (STCourseNode)node; + processStructureNode(processingFlag, structureNode, groups, areas); + } else if (node instanceof PortfolioCourseNode) { + PortfolioCourseNode portfolioNode = (PortfolioCourseNode)node; + processCondition(processingFlag, portfolioNode.getPreConditionEdit(), groups, areas); + } else if (node instanceof InfoCourseNode) { + InfoCourseNode infoNode = (InfoCourseNode)node; + processCondition(processingFlag, infoNode.getPreConditionEdit(), groups, areas); + processCondition(processingFlag, infoNode.getPreConditionAdmin(), groups, areas); + } else if (node instanceof DialogCourseNode) { + DialogCourseNode dialogNode = (DialogCourseNode)node; + processCondition(processingFlag, dialogNode.getPreConditionModerator(), groups, areas); + processCondition(processingFlag, dialogNode.getPreConditionPoster(), groups, areas); + processCondition(processingFlag, dialogNode.getPreConditionReader(), groups, areas); + } else if (node instanceof CalCourseNode) { + CalCourseNode calNode = (CalCourseNode)node; + processCondition(processingFlag, calNode.getPreConditionEdit(), groups, areas); + } else if (node instanceof COCourseNode) { + COCourseNode coNode = (COCourseNode)node; + processCONode(processingFlag, coNode, groups, areas); + } else if (node instanceof ENCourseNode) { + ENCourseNode enrollmentNode = (ENCourseNode)node; + processEnrollmentNode(processingFlag, enrollmentNode, groups, areas); + } + } + if(node instanceof TACourseNode) { + TACourseNode taskNode = (TACourseNode)node; + processCondition(processingFlag, taskNode.getConditionDrop(), groups, areas); + processCondition(processingFlag, taskNode.getConditionReturnbox(), groups, areas); + processCondition(processingFlag, taskNode.getConditionScoring(), groups, areas); + processCondition(processingFlag, taskNode.getConditionSolution(), groups, areas); + processCondition(processingFlag, taskNode.getConditionTask(), groups, areas); + } else if(node instanceof ProjectBrokerCourseNode) { + ProjectBrokerCourseNode brokerNode = (ProjectBrokerCourseNode)node; + processCondition(processingFlag, brokerNode.getConditionDrop(), groups, areas); + processCondition(processingFlag, brokerNode.getConditionReturnbox(), groups, areas); + processCondition(processingFlag, brokerNode.getConditionScoring(), groups, areas); + processCondition(processingFlag, brokerNode.getConditionProjectBroker(), groups, areas); + } else if (node instanceof FOCourseNode) { + FOCourseNode forumNode = (FOCourseNode)node; + processCondition(processingFlag, forumNode.getPreConditionModerator(), groups, areas); + processCondition(processingFlag, forumNode.getPreConditionPoster(), groups, areas); + processCondition(processingFlag, forumNode.getPreConditionReader(), groups, areas); + } else if (node instanceof BCCourseNode) { + BCCourseNode bcNode = (BCCourseNode)node; + processCondition(processingFlag, bcNode.getPreConditionDownloaders(), groups, areas); + processCondition(processingFlag, bcNode.getPreConditionUploaders(), groups, areas); + } else if (node instanceof AbstractFeedCourseNode) { + AbstractFeedCourseNode feedNode = (AbstractFeedCourseNode)node; + processCondition(processingFlag, feedNode.getPreConditionModerator(), groups, areas); + processCondition(processingFlag, feedNode.getPreConditionPoster(), groups, areas); + processCondition(processingFlag, feedNode.getPreConditionReader(), groups, areas); + } + } + } + private void processEnrollmentNode(ProcessingOccured processingFlag, ENCourseNode node, + List<BusinessGroup> groups, List<BGArea> areas) { + + ModuleConfiguration mc = node.getModuleConfiguration(); + String groupNames = (String)mc.get(ENCourseNode.CONFIG_GROUPNAME); + @SuppressWarnings("unchecked") + List<Long> groupKeys = (List<Long>) mc.get(ENCourseNode.CONFIG_GROUP_IDS); + if(groupKeys == null && StringHelper.containsNonWhitespace(groupNames)) { + List<Long> groupKeyList = toGroupKeyList(groupNames, groups); + mc.set(ENCourseNode.CONFIG_GROUP_IDS, groupKeyList); + processingFlag.processed(); + } + + String areaNames = (String)mc.get(ENCourseNode.CONFIG_AREANAME); + @SuppressWarnings("unchecked") + List<Long> areaKeys = (List<Long>) mc.get(ENCourseNode.CONFIG_AREA_IDS); + if(areaKeys == null && StringHelper.containsNonWhitespace(areaNames)) { + List<Long> areaKeyList = toAreaKeyList(areaNames, areas); + mc.set(ENCourseNode.CONFIG_AREA_IDS, areaKeyList); + processingFlag.processed(); + } + } + private void processCONode(ProcessingOccured processingFlag, COCourseNode coNode, + List<BusinessGroup> groups, List<BGArea> areas) { + + ModuleConfiguration mc = coNode.getModuleConfiguration(); + String groupNames = (String)mc.get(COEditController.CONFIG_KEY_EMAILTOGROUPS); + @SuppressWarnings("unchecked") + List<Long> groupKeys = (List<Long>) mc.get(COEditController.CONFIG_KEY_EMAILTOGROUP_IDS); + if(groupKeys == null && StringHelper.containsNonWhitespace(groupNames)) { + List<Long> groupKeyList = toGroupKeyList(groupNames, groups); + mc.set(COEditController.CONFIG_KEY_EMAILTOGROUP_IDS, groupKeyList); + processingFlag.processed(); + } + + String areaNames = (String)mc.get(COEditController.CONFIG_KEY_EMAILTOAREAS); + @SuppressWarnings("unchecked") + List<Long> areaKeys = (List<Long>) mc.get(COEditController.CONFIG_KEY_EMAILTOAREA_IDS); + if(areaKeys == null && StringHelper.containsNonWhitespace(areaNames)) { + List<Long> areaKeyList = toAreaKeyList(areaNames, areas); + mc.set(COEditController.CONFIG_KEY_EMAILTOAREA_IDS, areaKeyList); + processingFlag.processed(); + } + } + + private void processStructureNode(ProcessingOccured processingFlag, STCourseNode structureNode, + List<BusinessGroup> groups, List<BGArea> areas) { + + ScoreCalculator calculator = structureNode.getScoreCalculator(); + String score = calculator.getScoreExpression(); + String passed = calculator.getPassedExpression(); + + boolean stProcessed = false; + if(StringHelper.containsNonWhitespace(score)) { + String processedExpression = processExpression(processingFlag, score, groups, areas); + if(!processedExpression.equals(score)) { + calculator.setScoreExpression(processedExpression); + stProcessed = true; + } + } + + if(StringHelper.containsNonWhitespace(passed)) { + String processedExpression = processExpression(processingFlag, passed, groups, areas); + if(!processedExpression.equals(score)) { + calculator.setScoreExpression(processedExpression); + stProcessed = true; + } + } + + if(stProcessed) { + structureNode.setScoreCalculator(calculator); + } + } + + private void processCondition(ProcessingOccured processingFlag, Condition condition, + List<BusinessGroup> groups, List<BGArea> areas) { + boolean easy = StringHelper.containsNonWhitespace(condition.getConditionFromEasyModeConfiguration()); + if(easy) { + //already processed? + if(StringHelper.containsNonWhitespace(condition.getEasyModeGroupAccessIds()) + || StringHelper.containsNonWhitespace(condition.getEasyModeGroupAreaAccessIds())) { + return; + } + if(!StringHelper.containsNonWhitespace(condition.getEasyModeGroupAccess()) + && !StringHelper.containsNonWhitespace(condition.getEasyModeGroupAreaAccess())) { + return; + } + + String groupKeys = toGroupKeys(condition.getEasyModeGroupAccess(), groups); + condition.setEasyModeGroupAccessIds(groupKeys); + String areaKeys = toAreaKeys(condition.getEasyModeGroupAreaAccess(), areas); + condition.setEasyModeGroupAreaAccessIds(areaKeys); + processingFlag.processed(); + } else if(condition.isExpertMode()) { + String expression = condition.getConditionExpression(); + if(StringHelper.containsNonWhitespace(expression)) { + String reference = condition.getConditionExpression(); + String processExpression = processExpression(processingFlag, expression, groups, areas); + if(!reference.equals(processExpression)) { + condition.setConditionUpgraded(processExpression); + processingFlag.processed(); + } + } + } + } + + private String processExpression(ProcessingOccured processingFlag, String expression, + List<BusinessGroup> groups, List<BGArea> areas) { + + for(BusinessGroup group:groups) { + String strToMatch = "\"" + group.getName() + "\""; + String replacement = "\"" + group.getKey() + "\""; + expression = replaceAllCaseInsensitive(expression, strToMatch, replacement); + } + for(BGArea area:areas) { + String strToMatch = "\"" + area.getName() + "\""; + String replacement = "\"" + area.getKey() + "\""; + expression = replaceAllCaseInsensitive(expression, strToMatch, replacement); + } + + return expression; + } + + private String replaceAllCaseInsensitive(String expression, String name, String replacement) { + String lcExpresion = expression.toLowerCase(); + String lcName = name.toLowerCase(); + + int index = 0; + while((index = lcExpresion.indexOf(lcName, index)) > 0) { + int startIndex = index; + int stopIndex = index + lcName.length(); + + String newExpression = expression.substring(0, startIndex); + newExpression += replacement; + newExpression += expression.substring(stopIndex); + + expression = newExpression; + lcExpresion = expression.toLowerCase(); + index = startIndex + replacement.length(); + } + return expression; + } + + private String toGroupKeys(String groupNames, List<BusinessGroup> groups) { + if(!StringHelper.containsNonWhitespace(groupNames)) return null; + String[] groupNameArr = groupNames.split(","); + StringBuilder sb = new StringBuilder(); + for(String groupName:groupNameArr) { + groupName = groupName.trim(); + for(BusinessGroup group:groups) { + if(groupName.equalsIgnoreCase(group.getName())) { + if(sb.length() > 0) { + sb.append(','); + } + sb.append(group.getKey()); + break; + } + } + } + return sb.toString().trim(); + } + + private List<Long> toGroupKeyList(String groupNames, List<BusinessGroup> groups) { + if(!StringHelper.containsNonWhitespace(groupNames)) return null; + String[] groupNameArr = groupNames.split(","); + List<Long> groupKeyList = new ArrayList<Long>(); + for(String groupName:groupNameArr) { + groupName = groupName.trim(); + for(BusinessGroup group:groups) { + if(groupName.equalsIgnoreCase(group.getName())) { + groupKeyList.add(group.getKey()); + break; + } + } + } + return groupKeyList; + } + + private String toAreaKeys(String areaNames, List<BGArea> areas) { + if(!StringHelper.containsNonWhitespace(areaNames)) return null; + String[] areaNameArr = areaNames.split(","); + StringBuilder sb = new StringBuilder(); + for(String areaName:areaNameArr) { + areaName = areaName.trim(); + for(BGArea area:areas) { + if(areaName.equalsIgnoreCase(area.getName())) { + if(sb.length() > 0) { + sb.append(','); + } + sb.append(area.getKey()); + break; + } + } + } + return sb.toString().trim(); + } + + private List<Long> toAreaKeyList(String areaNames, List<BGArea> areas) { + if(!StringHelper.containsNonWhitespace(areaNames)) return null; + String[] areaNameArr = areaNames.split(","); + List<Long> areaKeyList = new ArrayList<Long>(); + for(String areaName:areaNameArr) { + areaName = areaName.trim(); + for(BGArea area:areas) { + if(areaName.equalsIgnoreCase(area.getName())) { + areaKeyList.add(area.getKey()); + break; + } + } + } + return areaKeyList; + } + + private class ProcessingOccured { + private boolean processed = false; + + public boolean isProcessed() { + return processed; + } + + public void processed() { + processed = true; + } + } } diff --git a/src/main/java/org/olat/course/PersistingCourseImpl.java b/src/main/java/org/olat/course/PersistingCourseImpl.java index 7307d0591167da7a5aaf4a144a586c98a9d39db0..b218a90607e71b474887e2d1d129368e553671b5 100644 --- a/src/main/java/org/olat/course/PersistingCourseImpl.java +++ b/src/main/java/org/olat/course/PersistingCourseImpl.java @@ -364,6 +364,7 @@ public class PersistingCourseImpl implements ICourse, OLATResourceable, Serializ // export configuration file FileUtils.copyFileToDir(new File(fCourseBase, CourseConfigManager.COURSECONFIG_XML), exportDirectory, "course export configuration and repo info"); // export learning groups + PersistingCourseGroupManager.getInstance(this).exportCourseBusinessGroups(fExportedDataDir); // export right groups //PersistingCourseGroupManager.getInstance(this).exportCourseRightGroups(fExportedDataDir); diff --git a/src/main/java/org/olat/course/Structure.java b/src/main/java/org/olat/course/Structure.java index 8312b0d2c12e90472b2cbe112a2486fd76ef14cc..5280d9526264fed17667b13a99ad71a4ab440ac7 100644 --- a/src/main/java/org/olat/course/Structure.java +++ b/src/main/java/org/olat/course/Structure.java @@ -39,7 +39,7 @@ import org.olat.course.nodes.CourseNode; public class Structure implements Serializable { private CourseNode rootNode = null; - transient private final static int CURRENTVERSION = 2; + transient private final static int CURRENTVERSION = 3; private int version; diff --git a/src/main/java/org/olat/course/_spring/courseContext.xml b/src/main/java/org/olat/course/_spring/courseContext.xml index e6d338367c10d1d7e95b1c1caf5ad47954386d02..8402c72e9e25fe0cf41fab6b54b7e31aeb0f24ec 100644 --- a/src/main/java/org/olat/course/_spring/courseContext.xml +++ b/src/main/java/org/olat/course/_spring/courseContext.xml @@ -118,4 +118,10 @@ </property> </bean> + +<bean id="courseUpgrade" class="org.olat.course.CourseUpgrade"> + <property name="areaManager" ref="areaManager"/> + <property name="businessGroupService" ref="businessGroupService"/> +</bean> + </beans> \ No newline at end of file diff --git a/src/main/java/org/olat/course/groupsandrights/PersistingCourseGroupManager.java b/src/main/java/org/olat/course/groupsandrights/PersistingCourseGroupManager.java index a91d88db6e3217e0bc341321f54ace0b8561a979..622ee900c817aa0a13fa930ad4b3c4f1c53c3c7d 100644 --- a/src/main/java/org/olat/course/groupsandrights/PersistingCourseGroupManager.java +++ b/src/main/java/org/olat/course/groupsandrights/PersistingCourseGroupManager.java @@ -307,16 +307,23 @@ public class PersistingCourseGroupManager extends BasicManager implements Course */ public void exportCourseBusinessGroups(File fExportDirectory) { File fExportFile = new File(fExportDirectory, LEARNINGGROUPEXPORT_XML); + List<BGArea> areas = getAllAreas(); List<BusinessGroup> groups = getAllBusinessGroups(); - businessGroupService.exportGroups(groups, fExportFile); + businessGroupService.exportGroups(groups, areas, fExportFile); } /** * @see org.olat.course.groupsandrights.CourseGroupManager#importCourseBusinessGroups(java.io.File) */ public void importCourseBusinessGroups(File fImportDirectory) { - File fGroupExportXML = new File(fImportDirectory, LEARNINGGROUPEXPORT_XML); - businessGroupService.importGroups(courseResource, fGroupExportXML); + File fGroupXML1 = new File(fImportDirectory, LEARNINGGROUPEXPORT_XML); + if(fGroupXML1.exists()) { + businessGroupService.importGroups(courseResource, fGroupXML1); + } + File fGroupXML2 = new File(fImportDirectory, RIGHTGROUPEXPORT_XML); + if(fGroupXML2.exists()) { + businessGroupService.importGroups(courseResource, fGroupXML2); + } } diff --git a/src/main/java/org/olat/course/nodes/co/COEditController.java b/src/main/java/org/olat/course/nodes/co/COEditController.java index 9ecbeaf16cbaedaf997f4fe742c5ce3334a17f85..e5f20f6610e9618c4c07b25ef3f8b8e30662b58a 100644 --- a/src/main/java/org/olat/course/nodes/co/COEditController.java +++ b/src/main/java/org/olat/course/nodes/co/COEditController.java @@ -109,7 +109,7 @@ public class COEditController extends ActivateableTabbableDefaultController impl // not needed: setInitialComponent(myContent); // Accessibility precondition Condition accessCondition = courseNode.getPreConditionAccess(); - accessibilityCondContr = new ConditionEditController(ureq, getWindowControl(), euce.getCourseEnvironment().getCourseGroupManager(), + accessibilityCondContr = new ConditionEditController(ureq, getWindowControl(), course.getCourseEnvironment().getCourseGroupManager(), accessCondition, "accessabilityConditionForm", AssessmentHelper.getAssessableNodes(course.getEditorTreeModel(), coCourseNode), euce); listenTo(accessibilityCondContr); diff --git a/src/main/java/org/olat/course/repository/ImportCourseController.java b/src/main/java/org/olat/course/repository/ImportCourseController.java index b7ab3049d1a7af2e21f35e79e09625652786c3b6..6182b5457a983ca2e0a5309b069e52c839e9bb09 100644 --- a/src/main/java/org/olat/course/repository/ImportCourseController.java +++ b/src/main/java/org/olat/course/repository/ImportCourseController.java @@ -35,6 +35,7 @@ import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.Constants; import org.olat.commons.file.filechooser.FileChooserController; +import org.olat.core.CoreSpringFactory; import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; @@ -52,6 +53,7 @@ import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.filters.VFSItemFileTypeFilter; import org.olat.course.CourseFactory; import org.olat.course.CourseModule; +import org.olat.course.CourseUpgrade; import org.olat.course.ICourse; import org.olat.course.Structure; import org.olat.course.config.CourseConfig; @@ -83,10 +85,12 @@ public class ImportCourseController extends BasicController implements IAddContr private Controller activeImportController; private ImportSharedfolderReferencesController sharedFolderImportController; private ImportGlossaryReferencesController glossaryImportController; - private List nodeList = new ArrayList(); + private List<CourseEditorTreeNode> nodeList = new ArrayList<CourseEditorTreeNode>(); private int nodeListPos = 0; private Panel myPanel; private static final VFSItemFileTypeFilter zipTypeFilter = new VFSItemFileTypeFilter(new String[] { "zip" }); + + private final CourseUpgrade courseUpgrade; /** * Import a course from a previous export. @@ -98,6 +102,7 @@ public class ImportCourseController extends BasicController implements IAddContr public ImportCourseController(RepositoryAddCallback callback, UserRequest ureq, WindowControl wControl) { super(ureq,wControl); this.callback = callback; + courseUpgrade = CoreSpringFactory.getImpl(CourseUpgrade.class); myPanel = new Panel("importPanel"); myPanel.addListener(this); @@ -126,6 +131,8 @@ public class ImportCourseController extends BasicController implements IAddContr CourseGroupManager cgm = course.getCourseEnvironment().getCourseGroupManager(); // import groups cgm.importCourseBusinessGroups(getExportDataDir(course)); + //upgrade to the current version of the course + courseUpgrade.processCourse(cgm.getCourseResource(), false); return true; } @@ -368,7 +375,7 @@ public class ImportCourseController extends BasicController implements IAddContr * @param rootNode * @param nl */ - public static void collectNodesAsList(CourseEditorTreeNode rootNode, List nl) { + private void collectNodesAsList(CourseEditorTreeNode rootNode, List<CourseEditorTreeNode> nl) { nl.add(rootNode); for (int i = 0; i < rootNode.getChildCount(); i++) { collectNodesAsList((CourseEditorTreeNode)rootNode.getChildAt(i), nl); diff --git a/src/main/java/org/olat/course/tree/CourseEditorTreeModel.java b/src/main/java/org/olat/course/tree/CourseEditorTreeModel.java index c4fdb0c7c637c321eed47190b41edd73cce7af7e..fc8918931327b9a76403065717bd7eea91c99bbd 100644 --- a/src/main/java/org/olat/course/tree/CourseEditorTreeModel.java +++ b/src/main/java/org/olat/course/tree/CourseEditorTreeModel.java @@ -48,7 +48,7 @@ public class CourseEditorTreeModel extends GenericTreeModel implements DnDTreeMo // zero -> meaning we read from an old // xml-structure which set it to zero, since it // did not exist - transient private final static int CURRENTVERSION = 2; + transient private final static int CURRENTVERSION = 3; private int version; private static OLog log = Tracing.createLoggerFor(CourseEditorTreeModel.class); diff --git a/src/main/java/org/olat/group/BusinessGroupService.java b/src/main/java/org/olat/group/BusinessGroupService.java index 457e66174a0d554229d1231fa49ef2638ad5fc8c..43cfa336f52194705c2dfa2c0b37565ec03db329 100644 --- a/src/main/java/org/olat/group/BusinessGroupService.java +++ b/src/main/java/org/olat/group/BusinessGroupService.java @@ -476,7 +476,7 @@ public interface BusinessGroupService { * @param groups * @param fExportFile */ - public void exportGroups(List<BusinessGroup> groups, File fExportFile); + public void exportGroups(List<BusinessGroup> groups, List<BGArea> areas, File fExportFile); /** * Import previously exported group definitions. diff --git a/src/main/java/org/olat/group/manager/BusinessGroupDeletionManager.java b/src/main/java/org/olat/group/manager/BusinessGroupDeletionManager.java index 1c0fca7975f1c9f899a809844fe730ad5776107d..11d70d5a28dc807ee03ac438b179bee5ed7deacb 100644 --- a/src/main/java/org/olat/group/manager/BusinessGroupDeletionManager.java +++ b/src/main/java/org/olat/group/manager/BusinessGroupDeletionManager.java @@ -48,6 +48,7 @@ import org.olat.core.util.mail.MailerResult; import org.olat.core.util.mail.MailerWithTemplate; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupService; +import org.olat.group.area.BGArea; import org.olat.repository.delete.service.DeletionModule; @@ -201,7 +202,7 @@ public class BusinessGroupDeletionManager extends BasicManager { } businessGroupService.archiveGroups(Collections.singletonList(businessGroup), new File(archiveFilePath, GROUPARCHIVE_XLS)); File exportFile = new File(archiveFilePath, GROUPEXPORT_XML); - businessGroupService.exportGroups(Collections.singletonList(businessGroup), exportFile); + businessGroupService.exportGroups(Collections.singletonList(businessGroup), Collections.<BGArea>emptyList(), exportFile); return GROUPEXPORT_XML; } diff --git a/src/main/java/org/olat/group/manager/BusinessGroupImportExport.java b/src/main/java/org/olat/group/manager/BusinessGroupImportExport.java index 1579722da97653e10fed62c518c0a39ee0bba569..db8ebd5a60819908b84473dc5b08af183ad837f8 100644 --- a/src/main/java/org/olat/group/manager/BusinessGroupImportExport.java +++ b/src/main/java/org/olat/group/manager/BusinessGroupImportExport.java @@ -64,7 +64,7 @@ public class BusinessGroupImportExport { private BusinessGroupPropertyDAO businessGroupPropertyManager; - public void exportGroups(List<BusinessGroup> groups, File fExportFile) { + public void exportGroups(List<BusinessGroup> groups, List<BGArea> areas, File fExportFile) { if (groups == null || groups.isEmpty()) return; // nothing to do... says Florian. @@ -72,6 +72,12 @@ public class BusinessGroupImportExport { // export areas root.setAreas(new AreaCollection()); root.getAreas().setGroups(new ArrayList<Area>()); + for (BGArea area : areas) { + Area newArea = new Area(); + newArea.name = area.getName(); + newArea.description = Collections.singletonList(area.getDescription()); + root.getAreas().getGroups().add(newArea); + } // export groups root.setGroups(new GroupCollection()); diff --git a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java index 265585bec98af8a123e540b960e61ad061a76712..3b3404fb0cd1bd30994d6d1a6c5addda216e0814 100644 --- a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java +++ b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java @@ -1228,8 +1228,8 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD } @Override - public void exportGroups(List<BusinessGroup> groups, File fExportFile) { - businessGroupImportExport.exportGroups(groups, fExportFile); + public void exportGroups(List<BusinessGroup> groups, List<BGArea> areas, File fExportFile) { + businessGroupImportExport.exportGroups(groups, areas, fExportFile); } @Override diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_8_2_0.java b/src/main/java/org/olat/upgrade/OLATUpgrade_8_2_0.java index d7e6c1fe01d232b61bfbb2f7309d394dec20f3ef..d9327801935e706a6c2e93a52373edab2435ef79 100644 --- a/src/main/java/org/olat/upgrade/OLATUpgrade_8_2_0.java +++ b/src/main/java/org/olat/upgrade/OLATUpgrade_8_2_0.java @@ -26,29 +26,7 @@ import java.util.List; import org.olat.basesecurity.BaseSecurity; import org.olat.core.commons.persistence.DB; import org.olat.core.id.Roles; -import org.olat.core.util.StringHelper; -import org.olat.core.util.nodes.INode; -import org.olat.course.CourseFactory; -import org.olat.course.ICourse; -import org.olat.course.Structure; -import org.olat.course.condition.Condition; -import org.olat.course.nodes.AbstractAccessableCourseNode; -import org.olat.course.nodes.AbstractFeedCourseNode; -import org.olat.course.nodes.BCCourseNode; -import org.olat.course.nodes.CalCourseNode; -import org.olat.course.nodes.CourseNode; -import org.olat.course.nodes.DialogCourseNode; -import org.olat.course.nodes.FOCourseNode; -import org.olat.course.nodes.GenericCourseNode; -import org.olat.course.nodes.InfoCourseNode; -import org.olat.course.nodes.PortfolioCourseNode; -import org.olat.course.nodes.ProjectBrokerCourseNode; -import org.olat.course.nodes.STCourseNode; -import org.olat.course.nodes.TACourseNode; -import org.olat.course.nodes.WikiCourseNode; -import org.olat.course.run.scoring.ScoreCalculator; -import org.olat.course.tree.CourseEditorTreeModel; -import org.olat.course.tree.CourseEditorTreeNode; +import org.olat.course.CourseUpgrade; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupImpl; import org.olat.group.BusinessGroupService; @@ -96,6 +74,8 @@ public class OLATUpgrade_8_2_0 extends OLATUpgrade { private OLATResourceManager resourceManager; @Autowired private RepositoryManager repositoryManager; + @Autowired + private CourseUpgrade courseUpgrade; public OLATUpgrade_8_2_0() { super(); @@ -185,7 +165,7 @@ public class OLATUpgrade_8_2_0 extends OLATUpgrade { do { entries = repositoryManager.genericANDQueryWithRolesRestriction(params, counter, REPO_ENTRIES_BATCH_SIZE, true); for(RepositoryEntry entry:entries) { - processCourse(entry); + courseUpgrade.processCourse(entry.getOlatResource(), true); } counter += entries.size(); log.audit("Processed repository entries: " + entries.size()); @@ -197,257 +177,6 @@ public class OLATUpgrade_8_2_0 extends OLATUpgrade { return true; } - private void processCourse(RepositoryEntry entry) { - ICourse opendedCourse = null; - try { - List<BusinessGroup> groups = businessGroupService.findBusinessGroups(null, entry.getOlatResource(), 0, -1); - List<BGArea> areas = areaManager.findBGAreasInContext(entry.getOlatResource()); - - ICourse course = CourseFactory.loadCourse(entry.getOlatResource()); - ProcessingOccured processingFlag = new ProcessingOccured(); - log.info("Start: " + course.getCourseTitle()); - Structure structure = course.getRunStructure(); - processCourseRec(processingFlag, structure.getRootNode(), groups, areas); - CourseEditorTreeModel editorTree = course.getEditorTreeModel(); - processEditorCourseRec(processingFlag, editorTree.getRootNode(), groups, areas); - if(processingFlag.isProcessed()) { - opendedCourse = CourseFactory.openCourseEditSession(course.getResourceableId()); - log.info("Save of:" + course.getCourseTitle() + " (" + course.getResourceableId() + ")"); - CourseFactory.saveCourse(opendedCourse.getResourceableId()); - } else { - log.info("No change for: " + course.getCourseTitle() + " (" + course.getResourceableId() + ")"); - } - } catch (Exception e) { - log.error("", e); - } finally { - if(opendedCourse != null) { - CourseFactory.closeCourseEditSession(opendedCourse.getResourceableId(), true); - } - } - } - - private void processCourseRec(ProcessingOccured processingFlag, INode node, - List<BusinessGroup> groups, List<BGArea> areas) { - if(node instanceof CourseNode) { - processCourseNode(processingFlag, (CourseNode)node, groups, areas); - } - for(int i=node.getChildCount(); i-->0; ) { - INode subNode = node.getChildAt(i); - processCourseRec(processingFlag, subNode, groups, areas); - } - } - - private void processEditorCourseRec(ProcessingOccured processingFlag, INode node, - List<BusinessGroup> groups, List<BGArea> areas) { - if(node instanceof CourseEditorTreeNode) { - processCourseNode(processingFlag, ((CourseEditorTreeNode)node).getCourseNode(), groups, areas); - } - for(int i=node.getChildCount(); i-->0; ) { - INode subNode = node.getChildAt(i); - processEditorCourseRec(processingFlag, subNode, groups, areas); - } - } - - /** - * Update all the conditions in every type of course node - * @param processingFlag - * @param node - * @param groups - * @param areas - */ - private void processCourseNode(ProcessingOccured processingFlag, CourseNode node, - List<BusinessGroup> groups, List<BGArea> areas) { - if(node instanceof GenericCourseNode) { - GenericCourseNode genericNode = (GenericCourseNode)node; - processCondition(processingFlag, genericNode.getPreConditionAccess(), groups, areas); - processCondition(processingFlag, genericNode.getPreConditionVisibility(), groups, areas); - if(node instanceof AbstractAccessableCourseNode) { - //nothing self but - - if(node instanceof WikiCourseNode) { - WikiCourseNode wikiNode = (WikiCourseNode)node; - processCondition(processingFlag, wikiNode.getPreConditionEdit(), groups, areas); - } else if(node instanceof STCourseNode) { - STCourseNode structureNode = (STCourseNode)node; - ScoreCalculator calculator = structureNode.getScoreCalculator(); - String score = calculator.getScoreExpression(); - String passed = calculator.getPassedExpression(); - - boolean stProcessed = false; - if(StringHelper.containsNonWhitespace(score)) { - String processedExpression = processExpression(processingFlag, score, groups, areas); - if(!processedExpression.equals(score)) { - calculator.setScoreExpression(processedExpression); - stProcessed = true; - } - } - - if(StringHelper.containsNonWhitespace(passed)) { - String processedExpression = processExpression(processingFlag, passed, groups, areas); - if(!processedExpression.equals(score)) { - calculator.setScoreExpression(processedExpression); - stProcessed = true; - } - } - - if(stProcessed) { - structureNode.setScoreCalculator(calculator); - } - } else if (node instanceof PortfolioCourseNode) { - PortfolioCourseNode portfolioNode = (PortfolioCourseNode)node; - processCondition(processingFlag, portfolioNode.getPreConditionEdit(), groups, areas); - } else if (node instanceof InfoCourseNode) { - InfoCourseNode infoNode = (InfoCourseNode)node; - processCondition(processingFlag, infoNode.getPreConditionEdit(), groups, areas); - processCondition(processingFlag, infoNode.getPreConditionAdmin(), groups, areas); - } else if (node instanceof DialogCourseNode) { - DialogCourseNode dialogNode = (DialogCourseNode)node; - processCondition(processingFlag, dialogNode.getPreConditionModerator(), groups, areas); - processCondition(processingFlag, dialogNode.getPreConditionPoster(), groups, areas); - processCondition(processingFlag, dialogNode.getPreConditionReader(), groups, areas); - } else if (node instanceof CalCourseNode) { - CalCourseNode calNode = (CalCourseNode)node; - processCondition(processingFlag, calNode.getPreConditionEdit(), groups, areas); - } - } - if(node instanceof TACourseNode) { - TACourseNode taskNode = (TACourseNode)node; - processCondition(processingFlag, taskNode.getConditionDrop(), groups, areas); - processCondition(processingFlag, taskNode.getConditionReturnbox(), groups, areas); - processCondition(processingFlag, taskNode.getConditionScoring(), groups, areas); - processCondition(processingFlag, taskNode.getConditionSolution(), groups, areas); - processCondition(processingFlag, taskNode.getConditionTask(), groups, areas); - } else if(node instanceof ProjectBrokerCourseNode) { - ProjectBrokerCourseNode brokerNode = (ProjectBrokerCourseNode)node; - processCondition(processingFlag, brokerNode.getConditionDrop(), groups, areas); - processCondition(processingFlag, brokerNode.getConditionReturnbox(), groups, areas); - processCondition(processingFlag, brokerNode.getConditionScoring(), groups, areas); - processCondition(processingFlag, brokerNode.getConditionProjectBroker(), groups, areas); - } else if (node instanceof FOCourseNode) { - FOCourseNode forumNode = (FOCourseNode)node; - processCondition(processingFlag, forumNode.getPreConditionModerator(), groups, areas); - processCondition(processingFlag, forumNode.getPreConditionPoster(), groups, areas); - processCondition(processingFlag, forumNode.getPreConditionReader(), groups, areas); - } else if (node instanceof BCCourseNode) { - BCCourseNode bcNode = (BCCourseNode)node; - processCondition(processingFlag, bcNode.getPreConditionDownloaders(), groups, areas); - processCondition(processingFlag, bcNode.getPreConditionUploaders(), groups, areas); - } else if (node instanceof AbstractFeedCourseNode) { - AbstractFeedCourseNode feedNode = (AbstractFeedCourseNode)node; - processCondition(processingFlag, feedNode.getPreConditionModerator(), groups, areas); - processCondition(processingFlag, feedNode.getPreConditionPoster(), groups, areas); - processCondition(processingFlag, feedNode.getPreConditionReader(), groups, areas); - } - } - } - - private void processCondition(ProcessingOccured processingFlag, Condition condition, - List<BusinessGroup> groups, List<BGArea> areas) { - boolean easy = StringHelper.containsNonWhitespace(condition.getConditionFromEasyModeConfiguration()); - if(easy) { - //already processed? - if(StringHelper.containsNonWhitespace(condition.getEasyModeGroupAccessIds()) - || StringHelper.containsNonWhitespace(condition.getEasyModeGroupAreaAccessIds())) { - return; - } - if(!StringHelper.containsNonWhitespace(condition.getEasyModeGroupAccess()) - && !StringHelper.containsNonWhitespace(condition.getEasyModeGroupAreaAccess())) { - return; - } - - String groupKeys = toGroupKeys(condition.getEasyModeGroupAccess(), groups); - condition.setEasyModeGroupAccessIds(groupKeys); - String areaKeys = toAreaKeys(condition.getEasyModeGroupAreaAccess(), areas); - condition.setEasyModeGroupAreaAccessIds(areaKeys); - processingFlag.processed(); - } else if(condition.isExpertMode()) { - String expression = condition.getConditionExpression(); - if(StringHelper.containsNonWhitespace(expression)) { - String reference = condition.getConditionExpression(); - String processExpression = processExpression(processingFlag, expression, groups, areas); - if(!reference.equals(processExpression)) { - condition.setConditionUpgraded(processExpression); - processingFlag.processed(); - } - } - } - } - - private String processExpression(ProcessingOccured processingFlag, String expression, - List<BusinessGroup> groups, List<BGArea> areas) { - - for(BusinessGroup group:groups) { - String strToMatch = "\"" + group.getName() + "\""; - String replacement = "\"" + group.getKey() + "\""; - expression = replaceAllCaseInsensitive(expression, strToMatch, replacement); - } - for(BGArea area:areas) { - String strToMatch = "\"" + area.getName() + "\""; - String replacement = "\"" + area.getKey() + "\""; - expression = replaceAllCaseInsensitive(expression, strToMatch, replacement); - } - - return expression; - } - - private String replaceAllCaseInsensitive(String expression, String name, String replacement) { - String lcExpresion = expression.toLowerCase(); - String lcName = name.toLowerCase(); - - int index = 0; - while((index = lcExpresion.indexOf(lcName, index)) > 0) { - int startIndex = index; - int stopIndex = index + lcName.length(); - - String newExpression = expression.substring(0, startIndex); - newExpression += replacement; - newExpression += expression.substring(stopIndex); - - expression = newExpression; - lcExpresion = expression.toLowerCase(); - index = startIndex + replacement.length(); - } - return expression; - } - - private String toGroupKeys(String groupNames, List<BusinessGroup> groups) { - if(!StringHelper.containsNonWhitespace(groupNames)) return null; - String[] groupNameArr = groupNames.split(","); - StringBuilder sb = new StringBuilder(); - for(String groupName:groupNameArr) { - groupName = groupName.trim(); - for(BusinessGroup group:groups) { - if(groupName.equalsIgnoreCase(group.getName())) { - if(sb.length() > 0) { - sb.append(','); - } - sb.append(group.getKey()); - break; - } - } - } - return sb.toString().trim(); - } - - private String toAreaKeys(String areaNames, List<BGArea> areas) { - if(!StringHelper.containsNonWhitespace(areaNames)) return null; - String[] areaNameArr = areaNames.split(","); - StringBuilder sb = new StringBuilder(); - for(String areaName:areaNameArr) { - areaName = areaName.trim(); - for(BGArea area:areas) { - if(areaName.equalsIgnoreCase(area.getName())) { - if(sb.length() > 0) { - sb.append(','); - } - sb.append(area.getKey()); - break; - } - } - } - return sb.toString().trim(); - } - private void processBusinessGroup(BusinessGroup group) { List<OLATResource> resources = findOLATResourcesForBusinessGroup(group); List<OLATResource> currentList = businessGroupService.findResources(Collections.singletonList(group), 0, -1); @@ -600,16 +329,4 @@ public class OLATUpgrade_8_2_0 extends OLATUpgrade { .getResultList(); return resources; } - - private class ProcessingOccured { - private boolean processed = false; - - public boolean isProcessed() { - return processed; - } - - public void processed() { - processed = true; - } - } }