From b873c052e0777df0435bd6ab24e6a79c3c3b14b5 Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Fri, 11 May 2018 09:20:08 +0200 Subject: [PATCH] OO-3290: implement moving curriculum elements within a curriculum --- .../modules/curriculum/CurriculumElement.java | 3 + .../curriculum/CurriculumElementType.java | 4 + .../modules/curriculum/CurriculumService.java | 14 + .../manager/CurriculumElementDAO.java | 16 ++ .../manager/CurriculumElementTypeDAO.java | 6 + .../manager/CurriculumServiceImpl.java | 13 + .../model/CurriculumElementImpl.java | 14 + .../model/CurriculumElementTypeImpl.java | 5 +- .../ui/CurriculumComposerController.java | 26 +- .../curriculum/ui/CurriculumTreeModel.java | 87 +++++++ .../ui/MoveCurriculumElementController.java | 245 ++++++++++++++++++ .../ui/_content/move_curriculum_element.html | 8 + .../database/mysql/alter_12_4_x_to_13_0_0.sql | 2 + .../database/mysql/setupDatabase.sql | 2 + .../oracle/alter_12_4_x_to_13_0_0.sql | 3 + .../database/oracle/setupDatabase.sql | 3 + .../postgresql/alter_12_4_x_to_13_0_0.sql | 4 + .../database/postgresql/setupDatabase.sql | 3 + .../manager/CurriculumElementTypeDAOTest.java | 14 + 19 files changed, 467 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/olat/modules/curriculum/ui/CurriculumTreeModel.java create mode 100644 src/main/java/org/olat/modules/curriculum/ui/MoveCurriculumElementController.java create mode 100644 src/main/java/org/olat/modules/curriculum/ui/_content/move_curriculum_element.html diff --git a/src/main/java/org/olat/modules/curriculum/CurriculumElement.java b/src/main/java/org/olat/modules/curriculum/CurriculumElement.java index 9338b8e489a..d0240145093 100644 --- a/src/main/java/org/olat/modules/curriculum/CurriculumElement.java +++ b/src/main/java/org/olat/modules/curriculum/CurriculumElement.java @@ -55,6 +55,9 @@ public interface CurriculumElement extends CurriculumElementRef, CreateInfo, Mod public CurriculumElement getParent(); + public CurriculumElementType getType(); + public Group getGroup(); + } diff --git a/src/main/java/org/olat/modules/curriculum/CurriculumElementType.java b/src/main/java/org/olat/modules/curriculum/CurriculumElementType.java index 48c4714e086..81eb55a87c1 100644 --- a/src/main/java/org/olat/modules/curriculum/CurriculumElementType.java +++ b/src/main/java/org/olat/modules/curriculum/CurriculumElementType.java @@ -19,6 +19,8 @@ */ package org.olat.modules.curriculum; +import java.util.Set; + import org.olat.core.id.CreateInfo; import org.olat.core.id.ModifiedInfo; @@ -45,5 +47,7 @@ public interface CurriculumElementType extends CurriculumElementTypeRef, CreateI public String getExternalId(); public void setExternalId(String externalId); + + public Set<CurriculumElementTypeToType> getAllowedSubTypes(); } diff --git a/src/main/java/org/olat/modules/curriculum/CurriculumService.java b/src/main/java/org/olat/modules/curriculum/CurriculumService.java index e565e15b1ac..6e600edadf5 100644 --- a/src/main/java/org/olat/modules/curriculum/CurriculumService.java +++ b/src/main/java/org/olat/modules/curriculum/CurriculumService.java @@ -53,6 +53,13 @@ public interface CurriculumService { public List<Curriculum> getCurriculums(CurriculumSearchParameters params); + /** + * The list of all types available. + * + * @return A list of curriculum element types + */ + public List<CurriculumElementType> getCurriculumElementTypes(); + public CurriculumElement createCurriculumElement(String identifier, String displayName, @@ -70,6 +77,13 @@ public interface CurriculumService { public CurriculumElement updateCurriculumElement(CurriculumElement element); + /** + * + * @param elementToMove The element to move + * @param newParent The new parent or null if root + */ + public CurriculumElement moveCurriculumElement(CurriculumElement elementToMove, CurriculumElement newParent); + /** * The list of members of the specified curriculum element. * diff --git a/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java b/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java index 2e29e1d998b..6c96cb3f1e7 100644 --- a/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java +++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java @@ -89,6 +89,22 @@ public class CurriculumElementDAO { return dbInstance.getCurrentEntityManager().merge(element); } + public CurriculumElement move(CurriculumElement element, CurriculumElement newParentElement) { + CurriculumElement parentLevel = element.getParent(); + if(parentLevel == null && newParentElement == null) { + return element;//already root + } else if(parentLevel != null && parentLevel.equals(newParentElement)) { + return element;//same parent + } + + CurriculumElementImpl elementImpl = (CurriculumElementImpl)element; + elementImpl.setParent(newParentElement); + elementImpl.setLastModified(new Date()); + elementImpl = dbInstance.getCurrentEntityManager().merge(elementImpl); + dbInstance.commit(); + return elementImpl; + } + public List<CurriculumElement> loadElements(CurriculumRef curriculum) { StringBuilder sb = new StringBuilder(256); sb.append("select el from curriculumelement el") diff --git a/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementTypeDAO.java b/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementTypeDAO.java index 5bdfad96963..99b6406ded9 100644 --- a/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementTypeDAO.java +++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementTypeDAO.java @@ -60,6 +60,12 @@ public class CurriculumElementTypeDAO { return types == null || types.isEmpty() ? null : types.get(0); } + public List<CurriculumElementType> load() { + return dbInstance.getCurrentEntityManager() + .createNamedQuery("loadCurriculumElementTypes", CurriculumElementType.class) + .getResultList(); + } + public CurriculumElementType update(CurriculumElementType type) { ((CurriculumElementTypeImpl)type).setLastModified(new Date()); return dbInstance.getCurrentEntityManager().merge(type); diff --git a/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java b/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java index d8cc0bef5d8..84d364b3fa7 100644 --- a/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java +++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java @@ -29,6 +29,7 @@ import org.olat.core.id.Organisation; import org.olat.modules.curriculum.Curriculum; import org.olat.modules.curriculum.CurriculumElement; import org.olat.modules.curriculum.CurriculumElementRef; +import org.olat.modules.curriculum.CurriculumElementType; import org.olat.modules.curriculum.CurriculumRef; import org.olat.modules.curriculum.CurriculumRoles; import org.olat.modules.curriculum.CurriculumService; @@ -59,6 +60,8 @@ public class CurriculumServiceImpl implements CurriculumService { @Autowired private CurriculumElementDAO curriculumElementDao; @Autowired + private CurriculumElementTypeDAO curriculumElementTypeDao; + @Autowired private RepositoryEntryRelationDAO repositoryEntryRelationDao; @Autowired private CurriculumRepositoryEntryRelationDAO curriculumRepositoryEntryRelationDao; @@ -78,6 +81,11 @@ public class CurriculumServiceImpl implements CurriculumService { return curriculumDao.update(curriculum); } + @Override + public List<CurriculumElementType> getCurriculumElementTypes() { + return curriculumElementTypeDao.load(); + } + @Override public List<Curriculum> getCurriculums(CurriculumSearchParameters params) { return curriculumDao.search(params); @@ -99,6 +107,11 @@ public class CurriculumServiceImpl implements CurriculumService { return curriculumElementDao.update(element); } + @Override + public CurriculumElement moveCurriculumElement(CurriculumElement elementToMove, CurriculumElement newParent) { + return curriculumElementDao.move(elementToMove, newParent); + } + @Override public List<CurriculumElement> getCurriculumElements(CurriculumRef curriculum) { return curriculumElementDao.loadElements(curriculum); diff --git a/src/main/java/org/olat/modules/curriculum/model/CurriculumElementImpl.java b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementImpl.java index dfda9261eaf..c57a7e1317b 100644 --- a/src/main/java/org/olat/modules/curriculum/model/CurriculumElementImpl.java +++ b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementImpl.java @@ -44,6 +44,7 @@ import org.olat.core.id.Persistable; import org.olat.modules.curriculum.Curriculum; import org.olat.modules.curriculum.CurriculumElement; import org.olat.modules.curriculum.CurriculumElementManagedFlag; +import org.olat.modules.curriculum.CurriculumElementType; /** * @@ -107,6 +108,10 @@ public class CurriculumElementImpl implements CurriculumElement, Persistable { @JoinColumn(name="fk_curriculum", nullable=true, insertable=true, updatable=true) private Curriculum curriculum; + @ManyToOne(targetEntity=CurriculumElementTypeImpl.class,fetch=FetchType.LAZY,optional=true) + @JoinColumn(name="fk_type", nullable=true, insertable=true, updatable=true) + private CurriculumElementType type; + @Override public Long getKey() { return key; @@ -247,6 +252,15 @@ public class CurriculumElementImpl implements CurriculumElement, Persistable { this.curriculum = curriculum; } + @Override + public CurriculumElementType getType() { + return type; + } + + public void setType(CurriculumElementType type) { + this.type = type; + } + @Override public int hashCode() { return key == null ? 28562153 : key.hashCode(); diff --git a/src/main/java/org/olat/modules/curriculum/model/CurriculumElementTypeImpl.java b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementTypeImpl.java index f60f4007623..e140bba9c1c 100644 --- a/src/main/java/org/olat/modules/curriculum/model/CurriculumElementTypeImpl.java +++ b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementTypeImpl.java @@ -48,9 +48,8 @@ import org.olat.modules.curriculum.CurriculumElementTypeToType; * */ @NamedQueries({ - @NamedQuery(name="loadCurriculumElementTypeByKey", query="select el from curriculumelementtype el where el.key=:key") - - + @NamedQuery(name="loadCurriculumElementTypeByKey", query="select el from curriculumelementtype el where el.key=:key"), + @NamedQuery(name="loadCurriculumElementTypes", query="select elementType from curriculumelementtype elementType") }) @Entity(name="curriculumelementtype") @Table(name="o_cur_element_type") diff --git a/src/main/java/org/olat/modules/curriculum/ui/CurriculumComposerController.java b/src/main/java/org/olat/modules/curriculum/ui/CurriculumComposerController.java index 6a8b9c5d46f..1ff6b07ed04 100644 --- a/src/main/java/org/olat/modules/curriculum/ui/CurriculumComposerController.java +++ b/src/main/java/org/olat/modules/curriculum/ui/CurriculumComposerController.java @@ -77,6 +77,7 @@ public class CurriculumComposerController extends FormBasicController implements private EditCurriculumElementController newElementCtrl; private CloseableCalloutWindowController toolsCalloutCtrl; private EditCurriculumElementController newSubElementCtrl; + private MoveCurriculumElementController moveElementCtrl; private int counter; private final Curriculum curriculum; @@ -164,7 +165,7 @@ public class CurriculumComposerController extends FormBasicController implements @Override protected void event(UserRequest ureq, Controller source, Event event) { - if(newElementCtrl == source || newSubElementCtrl == source) { + if(newElementCtrl == source || newSubElementCtrl == source || moveElementCtrl == source) { if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { loadModel(); } @@ -177,8 +178,10 @@ public class CurriculumComposerController extends FormBasicController implements } private void cleanUp() { + removeAsListenerAndDispose(moveElementCtrl); removeAsListenerAndDispose(newElementCtrl); removeAsListenerAndDispose(cmc); + moveElementCtrl = null; newElementCtrl = null; cmc = null; } @@ -257,6 +260,22 @@ public class CurriculumComposerController extends FormBasicController implements } } + private void doMoveCurriculumElement(UserRequest ureq, CurriculumElementRow row) { + CurriculumElement element = curriculumService.getCurriculumElement(row); + if(element == null) { + tableEl.reloadData(); + showWarning("warning.curriculum.element.deleted"); + } else { + List<CurriculumElement> elementsToMove = Collections.singletonList(element); + moveElementCtrl = new MoveCurriculumElementController(ureq, getWindowControl(), elementsToMove, curriculum); + listenTo(moveElementCtrl); + + cmc = new CloseableModalController(getWindowControl(), "close", moveElementCtrl.getInitialComponent(), true, translate("add.curriculum.element")); + listenTo(cmc); + cmc.activate(); + } + } + private void doOpenTools(UserRequest ureq, CurriculumElementRow row, FormLink link) { removeAsListenerAndDispose(toolsCtrl); removeAsListenerAndDispose(toolsCalloutCtrl); @@ -330,7 +349,10 @@ public class CurriculumComposerController extends FormBasicController implements if(editLink == source) { close(); doEditCurriculumElement(ureq, row); - } else if(deleteLink == source || moveLink == source) { + } else if(moveLink == source) { + close(); + doMoveCurriculumElement(ureq, row); + } else if(deleteLink == source) { close(); showWarning("Not implemented"); } else if(newLink == source) { diff --git a/src/main/java/org/olat/modules/curriculum/ui/CurriculumTreeModel.java b/src/main/java/org/olat/modules/curriculum/ui/CurriculumTreeModel.java new file mode 100644 index 00000000000..4bd5b926e36 --- /dev/null +++ b/src/main/java/org/olat/modules/curriculum/ui/CurriculumTreeModel.java @@ -0,0 +1,87 @@ +/** + * <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.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.olat.core.gui.components.tree.GenericTreeModel; +import org.olat.core.gui.components.tree.GenericTreeNode; +import org.olat.modules.curriculum.CurriculumElement; +import org.olat.modules.curriculum.CurriculumElementRef; + +/** + * + * Initial date: 11 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class CurriculumTreeModel extends GenericTreeModel { + + private static final long serialVersionUID = 2911319509933144413L; + + public static final String LEVEL_PREFIX = "cur-el-lev-"; + + public CurriculumTreeModel() { + GenericTreeNode root = new GenericTreeNode(); + root.setTitle("ROOT"); + setRootNode(root); + } + + public void loadTreeModel(List<CurriculumElement> elements) { + Map<Long,GenericTreeNode> fieldKeyToNode = new HashMap<>(); + for(CurriculumElement element:elements) { + Long key = element.getKey(); + GenericTreeNode node = fieldKeyToNode.computeIfAbsent(key, k -> { + GenericTreeNode newNode = new GenericTreeNode(nodeKey(element)); + newNode.setTitle(element.getDisplayName()); + newNode.setIconCssClass("o_icon_curriculum_element"); + newNode.setUserObject(element); + return newNode; + }); + + CurriculumElement parentElement = element.getParent(); + if(parentElement == null) { + //this is a root + getRootNode().addChild(node); + } else { + Long parentKey = parentElement.getKey(); + GenericTreeNode parentNode = fieldKeyToNode.computeIfAbsent(parentKey, k -> { + GenericTreeNode newNode = new GenericTreeNode(nodeKey(parentElement)); + newNode.setTitle(parentElement.getDisplayName()); + newNode.setIconCssClass("o_icon_curriculum_element"); + newNode.setUserObject(parentElement); + return newNode; + }); + + if(parentNode == null) { + fieldKeyToNode.put(parentKey, parentNode); + } else { + parentNode.addChild(node); + } + } + } + } + + public static final String nodeKey(CurriculumElementRef element) { + return LEVEL_PREFIX + element.getKey(); + } +} diff --git a/src/main/java/org/olat/modules/curriculum/ui/MoveCurriculumElementController.java b/src/main/java/org/olat/modules/curriculum/ui/MoveCurriculumElementController.java new file mode 100644 index 00000000000..8a1b5d6ea65 --- /dev/null +++ b/src/main/java/org/olat/modules/curriculum/ui/MoveCurriculumElementController.java @@ -0,0 +1,245 @@ +/** + * <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.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.tree.GenericTreeNode; +import org.olat.core.gui.components.tree.MenuTreeItem; +import org.olat.core.gui.components.tree.TreeNode; +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.util.nodes.INode; +import org.olat.modules.curriculum.Curriculum; +import org.olat.modules.curriculum.CurriculumElement; +import org.olat.modules.curriculum.CurriculumElementType; +import org.olat.modules.curriculum.CurriculumElementTypeToType; +import org.olat.modules.curriculum.CurriculumService; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 8 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class MoveCurriculumElementController extends FormBasicController { + + private MenuTreeItem curriculumTreeEl; + private final CurriculumTreeModel curriculumModel = new CurriculumTreeModel(); + + private final Curriculum curriculum; + private Set<CurriculumElementType> allowedTypes; + private List<CurriculumElement> curriculumElementsToMove; + private Set<TreeNode> targetableNodes = new HashSet<>(); + + @Autowired + private CurriculumService curriculumService; + + public MoveCurriculumElementController(UserRequest ureq, WindowControl wControl, + List<CurriculumElement> curriculumElementsToMove, Curriculum curriculum) { + super(ureq, wControl, "move_curriculum_element"); + this.curriculum = curriculum; + this.curriculumElementsToMove = new ArrayList<>(curriculumElementsToMove); + allowedTypes = getAllowedTypes(); + + initForm(ureq); + loadModel(); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + + curriculumTreeEl = uifactory.addTreeMultiselect("elements", null, formLayout, curriculumModel, this); + curriculumTreeEl.setMultiSelect(false); + curriculumTreeEl.setRootVisible(false); + + uifactory.addFormCancelButton("cancel", formLayout, ureq, getWindowControl()); + uifactory.addFormSubmitButton("move.element", formLayout); + } + + private void loadModel() { + List<CurriculumElement> allElements = curriculumService.getCurriculumElements(curriculum); + curriculumModel.loadTreeModel(allElements); + + //remove children of the curriculum element to move + for(CurriculumElement elementToMove:curriculumElementsToMove) { + TreeNode nodeToMove = curriculumModel + .getNodeById(CurriculumTreeModel.nodeKey(elementToMove)); + nodeToMove.removeAllChildren(); + if(nodeToMove.getParent() != null) { + nodeToMove.getParent().remove(nodeToMove); + } + } + + // remove the elements with incompatible types + List<TreeNode> openedNodes = new ArrayList<>(); + filterByAllowedTypes(curriculumModel.getRootNode(), openedNodes); + + List<String> nodeIds = openedNodes + .stream().map(TreeNode::getIdent) + .collect(Collectors.toList()); + curriculumTreeEl.setOpenNodeIds(nodeIds); + } + + private boolean filterByAllowedTypes(TreeNode node, List<TreeNode> openedNodes) { + ((GenericTreeNode)node).setIconCssClass(null); + + for(int i=node.getChildCount(); i-->0; ) { + boolean ok = filterByAllowedTypes((TreeNode)node.getChildAt(i), openedNodes); + if(!ok) { + node.remove(node.getChildAt(i)); + } + } + + boolean ok = false; + Object uobject = node.getUserObject(); + if(uobject instanceof CurriculumElement) { + CurriculumElement level = (CurriculumElement)uobject; + CurriculumElementType type = level.getType(); + if(type == null || allowedTypes.contains(type)) { + openedNodes.add(node); + ((GenericTreeNode)node).setIconCssClass("o_icon_node_under o_icon-rotate-180"); + targetableNodes.add(node); + ok = true; + } else if(node.getChildCount() > 0) { + openedNodes.add(node); + ok = true; + } + } else { + targetableNodes.add(node); + openedNodes.add(node); + ok = true; + } + + return ok; + } + + private Set<CurriculumElementType> getAllowedTypes() { + List<CurriculumElementType> allTypes = new ArrayList<>(curriculumService.getCurriculumElementTypes()); + Map<CurriculumElementType, Set<CurriculumElementType>> subToParentTypes = new HashMap<>(); + for(CurriculumElementType type:allTypes) { + Set<CurriculumElementTypeToType> typesToTypes = type.getAllowedSubTypes(); + for(CurriculumElementTypeToType typeToType:typesToTypes) { + CurriculumElementType subTyp = typeToType.getAllowedSubType(); + subToParentTypes + .computeIfAbsent(subTyp, t -> new HashSet<>()) + .add(type); + } + } + + Set<CurriculumElementType> analyzedTypes = new HashSet<>(); + for(CurriculumElement element:curriculumElementsToMove) { + CurriculumElementType levelType = element.getType(); + if(levelType != null && !analyzedTypes.contains(levelType)) { + analyzedTypes.add(levelType); + + Set<CurriculumElementType> allowed = subToParentTypes.get(levelType); + if(allowed != null) { + allTypes.retainAll(allowed); + } + } + } + + return new HashSet<>(allTypes); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected boolean validateFormLogic(UserRequest ureq) { + boolean allOk = super.validateFormLogic(ureq); + + curriculumTreeEl.clearError(); + if(curriculumTreeEl.getSelectedNode() == null) { + curriculumTreeEl.setErrorKey("error.select.target.level", null); + allOk &= false; + } else if(isParent()) { + curriculumTreeEl.setErrorKey("error.target.no.parent", null); + allOk &= false; + } else if(!targetableNodes.contains(curriculumTreeEl.getSelectedNode())) { + curriculumTreeEl.setErrorKey("error.target.not.allowed", null); + allOk &= false; + } + + return allOk; + } + + private boolean isParent() { + boolean parent = false; + for(CurriculumElement element:curriculumElementsToMove) { + parent |= isParent(element); + } + return parent; + } + + private boolean isParent(CurriculumElement element) { + TreeNode nodeToMove = curriculumModel + .getNodeById(CurriculumTreeModel.nodeKey(element)); + TreeNode selectedNode = curriculumTreeEl.getSelectedNode(); + if(selectedNode == curriculumModel.getRootNode()) { + return false;//can move to root + } + for(INode node=nodeToMove; node != null; node = node.getParent()) { + if(selectedNode == node) { + return true; + } + } + return false; + } + + @Override + protected void formCancelled(UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); + } + + @Override + protected void formOK(UserRequest ureq) { + if(isParent()) { + showWarning("error.target.no.parent"); + } else { + TreeNode selectedNode = curriculumTreeEl.getSelectedNode(); + if(selectedNode == curriculumModel.getRootNode()) { + for(CurriculumElement elementToMove:curriculumElementsToMove) { + curriculumService.moveCurriculumElement(elementToMove, null); + } + } else { + CurriculumElement newParent = (CurriculumElement)selectedNode.getUserObject(); + for(CurriculumElement elementToMove:curriculumElementsToMove) { + curriculumService.moveCurriculumElement(elementToMove, newParent); + } + } + } + fireEvent(ureq, Event.DONE_EVENT); + } +} diff --git a/src/main/java/org/olat/modules/curriculum/ui/_content/move_curriculum_element.html b/src/main/java/org/olat/modules/curriculum/ui/_content/move_curriculum_element.html new file mode 100644 index 00000000000..ac76ff73f22 --- /dev/null +++ b/src/main/java/org/olat/modules/curriculum/ui/_content/move_curriculum_element.html @@ -0,0 +1,8 @@ +$r.render("elements") +#if($f.hasError("elements")) +<div class="">$r.render("elements_ERROR")</div> +#end +<div class="o_button_group"> + $r.render("cancel") + $r.render("move.element") +</div> \ No newline at end of file diff --git a/src/main/resources/database/mysql/alter_12_4_x_to_13_0_0.sql b/src/main/resources/database/mysql/alter_12_4_x_to_13_0_0.sql index 7ab84a52418..f9acb5fc485 100644 --- a/src/main/resources/database/mysql/alter_12_4_x_to_13_0_0.sql +++ b/src/main/resources/database/mysql/alter_12_4_x_to_13_0_0.sql @@ -118,6 +118,7 @@ create table o_cur_curriculum_element ( fk_group bigint not null, fk_parent bigint, fk_curriculum bigint not null, + fk_type bigint, primary key (id) ); alter table o_cur_curriculum_element ENGINE = InnoDB; @@ -125,6 +126,7 @@ alter table o_cur_curriculum_element ENGINE = InnoDB; alter table o_cur_curriculum_element add constraint cur_el_to_group_idx foreign key (fk_group) references o_bs_group (id); alter table o_cur_curriculum_element add constraint cur_el_to_cur_el_idx foreign key (fk_parent) references o_cur_curriculum_element (id); alter table o_cur_curriculum_element add constraint cur_el_to_cur_idx foreign key (fk_curriculum) references o_cur_curriculum (id); +alter table o_cur_curriculum_element add constraint cur_el_type_to_el_type_idx foreign key (fk_type) references o_cur_element_type (id); create table o_cur_element_type_to_type ( id bigint not null auto_increment, diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql index 9cd0a890e89..bcdb983e542 100644 --- a/src/main/resources/database/mysql/setupDatabase.sql +++ b/src/main/resources/database/mysql/setupDatabase.sql @@ -2474,6 +2474,7 @@ fk_group bigint not null, fk_parent bigint, fk_curriculum bigint not null, + fk_type bigint, primary key (id) ); @@ -3430,6 +3431,7 @@ alter table o_cur_curriculum_element add constraint cur_el_to_group_idx foreign key (fk_group) references o_bs_group (id); alter table o_cur_curriculum_element add constraint cur_el_to_cur_el_idx foreign key (fk_parent) references o_cur_curriculum_element (id); alter table o_cur_curriculum_element add constraint cur_el_to_cur_idx foreign key (fk_curriculum) references o_cur_curriculum (id); + alter table o_cur_curriculum_element add constraint cur_el_type_to_el_type_idx foreign key (fk_type) references o_cur_element_type (id); alter table o_cur_element_type_to_type add constraint cur_type_to_type_idx foreign key (fk_type) references o_cur_element_type (id); alter table o_cur_element_type_to_type add constraint cur_type_to_sub_type_idx foreign key (fk_allowed_sub_type) references o_cur_element_type (id); diff --git a/src/main/resources/database/oracle/alter_12_4_x_to_13_0_0.sql b/src/main/resources/database/oracle/alter_12_4_x_to_13_0_0.sql index bb027d260cc..9f3de8b8043 100644 --- a/src/main/resources/database/oracle/alter_12_4_x_to_13_0_0.sql +++ b/src/main/resources/database/oracle/alter_12_4_x_to_13_0_0.sql @@ -120,6 +120,7 @@ create table o_cur_curriculum_element ( fk_group number(20) not null, fk_parent number(20), fk_curriculum number(20) not null, + fk_type number(20), primary key (id) ); @@ -129,6 +130,8 @@ alter table o_cur_curriculum_element add constraint cur_el_to_cur_el_idx foreign create index idx_cur_el_to_cur_el_idx on o_cur_curriculum_element (fk_parent); alter table o_cur_curriculum_element add constraint cur_el_to_cur_idx foreign key (fk_curriculum) references o_cur_curriculum (id); create index idx_cur_el_to_cur_idx on o_cur_curriculum_element (fk_curriculum); +alter table o_cur_curriculum_element add constraint cur_el_type_to_el_type_idx foreign key (fk_type) references o_cur_element_type (id); +create index idx_cur_el_type_to_el_type_idx on o_cur_curriculum_element (fk_type); create table o_cur_element_type_to_type ( id number(20) generated always as identity, diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql index c641dda48cd..52dc3b499f0 100644 --- a/src/main/resources/database/oracle/setupDatabase.sql +++ b/src/main/resources/database/oracle/setupDatabase.sql @@ -2518,6 +2518,7 @@ create table o_cur_curriculum_element ( fk_group number(20) not null, fk_parent number(20), fk_curriculum number(20) not null, + fk_type number(20), primary key (id) ); @@ -3632,6 +3633,8 @@ alter table o_cur_curriculum_element add constraint cur_el_to_cur_el_idx foreign create index idx_cur_el_to_cur_el_idx on o_cur_curriculum_element (fk_parent); alter table o_cur_curriculum_element add constraint cur_el_to_cur_idx foreign key (fk_curriculum) references o_cur_curriculum (id); create index idx_cur_el_to_cur_idx on o_cur_curriculum_element (fk_curriculum); +alter table o_cur_curriculum_element add constraint cur_el_type_to_el_type_idx foreign key (fk_type) references o_cur_element_type (id); +create index idx_cur_el_type_to_el_type_idx on o_cur_curriculum_element (fk_type); alter table o_cur_element_type_to_type add constraint cur_type_to_type_idx foreign key (fk_type) references o_cur_element_type (id); create index idx_cur_type_to_type_idx on o_cur_element_type_to_type (fk_type); diff --git a/src/main/resources/database/postgresql/alter_12_4_x_to_13_0_0.sql b/src/main/resources/database/postgresql/alter_12_4_x_to_13_0_0.sql index a4c514d8552..61067187a4e 100644 --- a/src/main/resources/database/postgresql/alter_12_4_x_to_13_0_0.sql +++ b/src/main/resources/database/postgresql/alter_12_4_x_to_13_0_0.sql @@ -120,6 +120,7 @@ create table o_cur_curriculum_element ( fk_group int8 not null, fk_parent int8, fk_curriculum int8 not null, + fk_type int8, primary key (id) ); @@ -129,6 +130,9 @@ alter table o_cur_curriculum_element add constraint cur_el_to_cur_el_idx foreign create index idx_cur_el_to_cur_el_idx on o_cur_curriculum_element (fk_parent); alter table o_cur_curriculum_element add constraint cur_el_to_cur_idx foreign key (fk_curriculum) references o_cur_curriculum (id); create index idx_cur_el_to_cur_idx on o_cur_curriculum_element (fk_curriculum); +alter table o_cur_curriculum_element add constraint cur_el_type_to_el_type_idx foreign key (fk_type) references o_cur_element_type (id); +create index idx_cur_el_type_to_el_type_idx on o_cur_curriculum_element (fk_type); + create table o_cur_element_type_to_type ( id bigserial, diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql index 6c0fac4ccde..2eaaa09fc4a 100644 --- a/src/main/resources/database/postgresql/setupDatabase.sql +++ b/src/main/resources/database/postgresql/setupDatabase.sql @@ -2471,6 +2471,7 @@ create table o_cur_curriculum_element ( fk_group int8 not null, fk_parent int8, fk_curriculum int8 not null, + fk_type int8, primary key (id) ); @@ -3485,6 +3486,8 @@ alter table o_cur_curriculum_element add constraint cur_el_to_cur_el_idx foreign create index idx_cur_el_to_cur_el_idx on o_cur_curriculum_element (fk_parent); alter table o_cur_curriculum_element add constraint cur_el_to_cur_idx foreign key (fk_curriculum) references o_cur_curriculum (id); create index idx_cur_el_to_cur_idx on o_cur_curriculum_element (fk_curriculum); +alter table o_cur_curriculum_element add constraint cur_el_type_to_el_type_idx foreign key (fk_type) references o_cur_element_type (id); +create index idx_cur_el_type_to_el_type_idx on o_cur_curriculum_element (fk_type); alter table o_cur_element_type_to_type add constraint cur_type_to_type_idx foreign key (fk_type) references o_cur_element_type (id); create index idx_cur_type_to_type_idx on o_cur_element_type_to_type (fk_type); diff --git a/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementTypeDAOTest.java b/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementTypeDAOTest.java index 6dc5e8247b6..0fa430ab801 100644 --- a/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementTypeDAOTest.java +++ b/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementTypeDAOTest.java @@ -83,6 +83,20 @@ public class CurriculumElementTypeDAOTest extends OlatTestCase { Assert.assertEquals("AC-235", reloadedType.getExternalId()); } + @Test + public void loadAlltypes() { + CurriculumElementType type = curriculumElementTypeDao.createCurriculumElementType("cur-el-3", "3. Element", "Third element", "AC-236"); + Assert.assertNotNull(type); + dbInstance.commitAndCloseSession(); + + // load the element type + List<CurriculumElementType> allTypes = curriculumElementTypeDao.load(); + dbInstance.commitAndCloseSession(); + //check + Assert.assertNotNull(allTypes); + Assert.assertTrue(allTypes.contains(type)); + } + @Test public void allowSubTypes() { CurriculumElementType type = curriculumElementTypeDao.createCurriculumElementType("Type-parent", "A type", null, null); -- GitLab