From c4cdff52225a130f9e5664d4052d42625d2d64db Mon Sep 17 00:00:00 2001
From: srosse <stephane.rosse@frentix.com>
Date: Fri, 22 Mar 2019 17:00:14 +0100
Subject: [PATCH] OO-3980: allow to move root cur. elements from a curriculum
 to an other

---
 .../modules/curriculum/CurriculumService.java |  10 ++
 .../manager/CurriculumElementDAO.java         |  42 +++++++-
 .../manager/CurriculumServiceImpl.java        |   7 ++
 .../restapi/CurriculumElementsWebService.java |  12 ++-
 .../manager/CurriculumElementDAOTest.java     | 101 ++++++++++++++++++
 .../CurriculumElementsWebServiceTest.java     |  72 +++++++++++++
 6 files changed, 238 insertions(+), 6 deletions(-)

diff --git a/src/main/java/org/olat/modules/curriculum/CurriculumService.java b/src/main/java/org/olat/modules/curriculum/CurriculumService.java
index 7cf7e87852c..f9b4ce0e901 100644
--- a/src/main/java/org/olat/modules/curriculum/CurriculumService.java
+++ b/src/main/java/org/olat/modules/curriculum/CurriculumService.java
@@ -322,6 +322,16 @@ public interface CurriculumService {
 	 */
 	public CurriculumElement moveCurriculumElement(CurriculumElement elementToMove, CurriculumElement newParent, CurriculumElement siblingBefore);
 	
+	/**
+	 * Move a root curriculum element from a curriculum to an other. This operation
+	 * is committed asap.
+	 * 
+	 * @param rootElement The curriculum element (must be a root one)
+	 * @param curriculum The target curriculum
+	 * @return The update element
+	 */
+	public CurriculumElement moveCurriculumElement(CurriculumElement rootElement, Curriculum curriculum);
+	
 	/**
 	 * 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 d881917928d..5c172fe8d2a 100644
--- a/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java
+++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java
@@ -184,6 +184,34 @@ public class CurriculumElementDAO {
 		return dbInstance.getCurrentEntityManager().merge(element);
 	}
 	
+	public CurriculumElement move(CurriculumElement rootElementToMove, Curriculum newCurriculum) {
+		CurriculumElementImpl rootElement = (CurriculumElementImpl)rootElementToMove;
+		List<CurriculumElement> descendants = getDescendants(rootElement);// load the descendants with the old curriculum
+		descendants.remove(rootElementToMove);
+
+		CurriculumImpl oldCurriculum = loadCurriculumByKey(rootElement);
+		if(oldCurriculum == null) return rootElement;
+		oldCurriculum.getRootElements().remove(rootElement);
+		rootElement.setPosCurriculum(null);
+		rootElement.setCurriculumParent(null);
+		rootElement = dbInstance.getCurrentEntityManager().merge(rootElement);
+		dbInstance.getCurrentEntityManager().merge(oldCurriculum);
+
+		CurriculumImpl curriculum = loadCurriculumByKey(newCurriculum.getKey());
+		curriculum.getRootElements().add(rootElement);
+		rootElement.setCurriculum(curriculum);
+		rootElement.setPosCurriculum(Long.valueOf(curriculum.getRootElements().size()));
+		rootElement.setCurriculumParent(curriculum);
+		rootElement = dbInstance.getCurrentEntityManager().merge(rootElement);
+		curriculum = dbInstance.getCurrentEntityManager().merge(curriculum);
+		
+		for(CurriculumElement descendant:descendants) {
+			((CurriculumElementImpl)descendant).setCurriculum(curriculum);
+			dbInstance.getCurrentEntityManager().merge(descendant);
+		}
+		return rootElement;
+	}	
+	
 	public CurriculumElement move(CurriculumElement elementToMove, CurriculumElement newParentElement, CurriculumElement siblingBefore) {
 		CurriculumElement parentElement = elementToMove.getParent();
 		CurriculumElementImpl element = (CurriculumElementImpl)elementToMove;
@@ -191,14 +219,14 @@ public class CurriculumElementDAO {
 		if(parentElement == null && newParentElement == null) {
 			// reorder curriculum children
 			
-			CurriculumImpl curriculum = loadCurriculunByKey(element);
+			CurriculumImpl curriculum = loadCurriculumByKey(element);
 			List<CurriculumElement> rootElements = curriculum.getRootElements();
 			reorderList(element, rootElements, siblingBefore);
 			dbInstance.getCurrentEntityManager().merge(curriculum);
 		} else if(parentElement == null) {
 			// move from curriculum as root to a curriculum element
 			
-			CurriculumImpl curriculum = loadCurriculunByKey(element);
+			CurriculumImpl curriculum = loadCurriculumByKey(element);
 			List<CurriculumElement> rootElements = curriculum.getRootElements();
 			element.setCurriculumParent(null);
 			rootElements.remove(element);
@@ -218,7 +246,7 @@ public class CurriculumElementDAO {
 			element.setParent(null);
 			dbInstance.getCurrentEntityManager().merge(parentElement);	
 			
-			CurriculumImpl curriculum = loadCurriculunByKey(element);
+			CurriculumImpl curriculum = loadCurriculumByKey(element);
 			element.setCurriculumParent(curriculum);
 			List<CurriculumElement> rootElements = curriculum.getRootElements();
 			reorderList(element, rootElements, siblingBefore);
@@ -291,14 +319,18 @@ public class CurriculumElementDAO {
 		}
 	}
 	
-	private CurriculumImpl loadCurriculunByKey(CurriculumElement element) {
+	private CurriculumImpl loadCurriculumByKey(CurriculumElement element) {
+		return loadCurriculumByKey(element.getCurriculum().getKey());
+	}
+	
+	private CurriculumImpl loadCurriculumByKey(Long curriculumKey) {
 		StringBuilder sb = new StringBuilder(128);
 		sb.append("select cur from curriculum cur")
 		  .append(" where cur.key=:key");
 		
 		List<CurriculumImpl> curriculums = dbInstance.getCurrentEntityManager()
 			.createQuery(sb.toString(), CurriculumImpl.class)
-			.setParameter("key", element.getCurriculum().getKey())
+			.setParameter("key", curriculumKey)
 			.getResultList();
 		return curriculums == null || curriculums.isEmpty() ? null : curriculums.get(0);
 	}
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 e66887e70df..a509c633d12 100644
--- a/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java
+++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java
@@ -382,6 +382,13 @@ public class CurriculumServiceImpl implements CurriculumService, OrganisationDat
 		return curriculumElementDao.move(elementToMove, newParent, siblingBefore);
 	}
 
+	@Override
+	public CurriculumElement moveCurriculumElement(CurriculumElement rootElement, Curriculum curriculum) {
+		CurriculumElement element = curriculumElementDao.move(rootElement, curriculum);
+		dbInstance.commit();
+		return element;
+	}
+
 	@Override
 	public List<CurriculumElement> getCurriculumElements(CurriculumRef curriculum, CurriculumElementStatus[] status) {
 		return curriculumElementDao.loadElements(curriculum, status);
diff --git a/src/main/java/org/olat/modules/curriculum/restapi/CurriculumElementsWebService.java b/src/main/java/org/olat/modules/curriculum/restapi/CurriculumElementsWebService.java
index 4acd045a405..f5cc47b7ecc 100644
--- a/src/main/java/org/olat/modules/curriculum/restapi/CurriculumElementsWebService.java
+++ b/src/main/java/org/olat/modules/curriculum/restapi/CurriculumElementsWebService.java
@@ -91,7 +91,7 @@ public class CurriculumElementsWebService {
 	@Autowired
 	private CurriculumElementToTaxonomyLevelDAO curriculumElementToTaxonomyLevelDao;
 	
-	private final Curriculum curriculum;
+	private Curriculum curriculum;
 	
 	public CurriculumElementsWebService(Curriculum curriculum) {
 		this.curriculum = curriculum;
@@ -244,6 +244,7 @@ public class CurriculumElementsWebService {
 		}
 		
 		boolean move = false;
+		boolean moveAsCurriculumRoot = false;
 		if(curriculumElement.getKey() == null) {
 			elementToSave = curriculumService.createCurriculumElement(curriculumElement.getIdentifier(), curriculumElement.getDisplayName(),
 					null, curriculumElement.getBeginDate(), curriculumElement.getEndDate(), parentElement, type,
@@ -258,6 +259,10 @@ public class CurriculumElementsWebService {
 			if(parentElement != null && elementToSave.getParent() != null
 					&& !elementToSave.getParent().getKey().equals(parentElement.getKey())) {
 				move = true;
+			} else if(parentElement == null && elementToSave.getParent() == null
+					&& (elementToSave.getCurriculum() != null && !elementToSave.getCurriculum().getKey().equals(curriculum.getKey()))) {
+				// this is a root curriculum element and it get a new curriculum as home
+				moveAsCurriculumRoot = true;
 			}
 		}
 		
@@ -277,6 +282,11 @@ public class CurriculumElementsWebService {
 			curriculumService.moveCurriculumElement(savedElement, parentElement, null);
 			dbInstance.commit();
 			savedElement = curriculumService.getCurriculumElement(savedElement);
+		} else if(moveAsCurriculumRoot) {
+			dbInstance.commit();// make sure all is flushed on the database before such a move
+			curriculum = curriculumService.getCurriculum(curriculum);
+			System.out.println(curriculum.getDisplayName());
+			savedElement = curriculumService.moveCurriculumElement(savedElement, curriculum);
 		}
 		return savedElement;
 	}
diff --git a/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementDAOTest.java b/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementDAOTest.java
index bee234f655b..122907d38a6 100644
--- a/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementDAOTest.java
+++ b/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementDAOTest.java
@@ -603,7 +603,108 @@ public class CurriculumElementDAOTest extends OlatTestCase {
 		Assert.assertEquals(element1_1_3, rootElements.get(1));
 	}
 	
+	@Test
+	public void moveRootCurriculumElement_curriculumToCurriculum() {
+		Curriculum curriculum = curriculumDao.createAndPersist("cur-start-", "Curriculum to start", "Curriculum", null);
+		Curriculum targetCurriculum = curriculumDao.createAndPersist("cur-new-home-", "Curriculum as new home", "Curriculum", null);
+
+		CurriculumElement element1 = curriculumElementDao.createCurriculumElement("Element-1", "1. Element", CurriculumElementStatus.active,
+				null, null, null, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		CurriculumElement element1_1 = curriculumElementDao.createCurriculumElement("Element-1-1", "1.1. Element", CurriculumElementStatus.active,
+				null, null, element1, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		CurriculumElement element1_2 = curriculumElementDao.createCurriculumElement("Element-1-2", "1.2. Element", CurriculumElementStatus.active,
+				null, null, element1, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		CurriculumElement element1_2_1 = curriculumElementDao.createCurriculumElement("Element-1-2-1", "1.2.1. Element", CurriculumElementStatus.active,
+				null, null, element1_2, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		CurriculumElement element1_2_2 = curriculumElementDao.createCurriculumElement("Element-1-2-2", "1.2.2 Element", CurriculumElementStatus.active,
+				null, null, element1_2, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		dbInstance.commitAndCloseSession();
+		
+		// move element1 under its new curriculum
+		curriculumElementDao.move(element1, targetCurriculum);
+		dbInstance.commitAndCloseSession();
+		
+		// check the source curriculum
+		CurriculumImpl reloadedCurriculum = (CurriculumImpl)curriculumDao.loadByKey(curriculum.getKey());
+		List<CurriculumElement> rootElements = reloadedCurriculum.getRootElements();
+		Assert.assertTrue(rootElements.isEmpty());
+		
+		// check the target curriculum
+		CurriculumImpl reloadedTargetCurriculum = (CurriculumImpl)curriculumDao.loadByKey(targetCurriculum.getKey());
+		List<CurriculumElement> targetRootElements = reloadedTargetCurriculum.getRootElements();
+		Assert.assertEquals(1, targetRootElements.size());
+		Assert.assertTrue(targetRootElements.contains(element1));
+		
+		List<CurriculumElement> movedElements = curriculumElementDao.loadElements(reloadedTargetCurriculum, CurriculumElementStatus.values());
+		Assert.assertTrue(movedElements.contains(element1));
+		Assert.assertTrue(movedElements.contains(element1_1));
+		Assert.assertTrue(movedElements.contains(element1_2));
+		Assert.assertTrue(movedElements.contains(element1_2_1));
+		Assert.assertTrue(movedElements.contains(element1_2_2));
+	}
 	
+	@Test
+	public void moveRootCurriculumElement_curriculumToCurriculum_v2() {
+		Curriculum curriculum = curriculumDao.createAndPersist("cur-start-", "Curriculum to start", "Curriculum", null);
+		Curriculum targetCurriculum = curriculumDao.createAndPersist("cur-new-home-", "Curriculum as new home", "Curriculum", null);
+
+		CurriculumElement element1 = curriculumElementDao.createCurriculumElement("Element-1", "1. Element", CurriculumElementStatus.active,
+				null, null, null, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		CurriculumElement element1_1 = curriculumElementDao.createCurriculumElement("Element-1-1", "1.1. Element", CurriculumElementStatus.active,
+				null, null, element1, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		CurriculumElement element1_2 = curriculumElementDao.createCurriculumElement("Element-1-2", "1.2. Element", CurriculumElementStatus.active,
+				null, null, element1, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		CurriculumElement element1_2_1 = curriculumElementDao.createCurriculumElement("Element-1-2-1", "1.2.1. Element", CurriculumElementStatus.active,
+				null, null, element1_2, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		CurriculumElement element1_2_2 = curriculumElementDao.createCurriculumElement("Element-1-2-2", "1.2.2 Element", CurriculumElementStatus.active,
+				null, null, element1_2, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		
+		CurriculumElement stayingElement1 = curriculumElementDao.createCurriculumElement("T-Element-1", "1. Element", CurriculumElementStatus.active,
+				null, null, null, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		CurriculumElement stayingElement1_1 = curriculumElementDao.createCurriculumElement("T-Element-1-1", "1.1. Element", CurriculumElementStatus.active,
+				null, null, stayingElement1, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, curriculum);
+		
+		CurriculumElement targetElement1 = curriculumElementDao.createCurriculumElement("T-Element-1", "1. Element", CurriculumElementStatus.active,
+				null, null, null, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, targetCurriculum);
+		CurriculumElement targetElement1_1 = curriculumElementDao.createCurriculumElement("T-Element-1-1", "1.1. Element", CurriculumElementStatus.active,
+				null, null, targetElement1, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, targetCurriculum);
+		dbInstance.commitAndCloseSession();
+		
+		// move element1 under its new curriculum
+		curriculumElementDao.move(element1, targetCurriculum);
+		dbInstance.commitAndCloseSession();
+		
+		// check the source curriculum
+		CurriculumImpl reloadedCurriculum = (CurriculumImpl)curriculumDao.loadByKey(curriculum.getKey());
+		List<CurriculumElement> rootElements = reloadedCurriculum.getRootElements();
+		Assert.assertEquals(1, rootElements.size());
+		Assert.assertTrue(rootElements.contains(stayingElement1));
+		
+		// check the target curriculum
+		CurriculumImpl reloadedTargetCurriculum = (CurriculumImpl)curriculumDao.loadByKey(targetCurriculum.getKey());
+		List<CurriculumElement> targetRootElements = reloadedTargetCurriculum.getRootElements();
+		Assert.assertEquals(2, targetRootElements.size());
+		Assert.assertTrue(targetRootElements.contains(targetElement1));
+		Assert.assertTrue(targetRootElements.contains(element1));
+		
+		List<CurriculumElement> targetElements = curriculumElementDao.loadElements(reloadedTargetCurriculum, CurriculumElementStatus.values());
+		Assert.assertEquals(7, targetElements.size());
+		// current ones
+		Assert.assertTrue(targetElements.contains(targetElement1));
+		Assert.assertTrue(targetElements.contains(targetElement1_1));
+		// moved ones
+		Assert.assertTrue(targetElements.contains(element1));
+		Assert.assertTrue(targetElements.contains(element1_1));
+		Assert.assertTrue(targetElements.contains(element1_2));
+		Assert.assertTrue(targetElements.contains(element1_2_1));
+		Assert.assertTrue(targetElements.contains(element1_2_2));
+		
+		List<CurriculumElement> stayingElements = curriculumElementDao.loadElements(curriculum, CurriculumElementStatus.values());
+		Assert.assertEquals(2, stayingElements.size());
+		// current ones
+		Assert.assertTrue(stayingElements.contains(stayingElement1));
+		Assert.assertTrue(stayingElements.contains(stayingElement1_1));
+	}
 	
 	@Test
 	public void getMembersIdentity() {
diff --git a/src/test/java/org/olat/restapi/CurriculumElementsWebServiceTest.java b/src/test/java/org/olat/restapi/CurriculumElementsWebServiceTest.java
index ab7beeb9883..ac4f330618d 100644
--- a/src/test/java/org/olat/restapi/CurriculumElementsWebServiceTest.java
+++ b/src/test/java/org/olat/restapi/CurriculumElementsWebServiceTest.java
@@ -57,8 +57,11 @@ import org.olat.modules.curriculum.CurriculumElementType;
 import org.olat.modules.curriculum.CurriculumLectures;
 import org.olat.modules.curriculum.CurriculumRoles;
 import org.olat.modules.curriculum.CurriculumService;
+import org.olat.modules.curriculum.manager.CurriculumDAO;
+import org.olat.modules.curriculum.manager.CurriculumElementDAO;
 import org.olat.modules.curriculum.manager.CurriculumElementToTaxonomyLevelDAO;
 import org.olat.modules.curriculum.model.CurriculumElementRefImpl;
+import org.olat.modules.curriculum.model.CurriculumImpl;
 import org.olat.modules.curriculum.model.CurriculumMember;
 import org.olat.modules.curriculum.model.SearchMemberParameters;
 import org.olat.modules.curriculum.restapi.CurriculumElementMemberVO;
@@ -92,10 +95,14 @@ public class CurriculumElementsWebServiceTest extends OlatJerseyTestCase {
 	@Autowired
 	private TaxonomyDAO taxonomyDao;
 	@Autowired
+	private CurriculumDAO curriculumDao;
+	@Autowired
 	private TaxonomyLevelDAO taxonomyLevelDao;
 	@Autowired
 	private CurriculumService curriculumService;
 	@Autowired
+	private CurriculumElementDAO curriculumElementDao;
+	@Autowired
 	private OrganisationService organisationService;
 
 	@Autowired
@@ -321,6 +328,71 @@ public class CurriculumElementsWebServiceTest extends OlatJerseyTestCase {
 		Assert.assertEquals(type, savedElement.getType());
 	}
 	
+
+	@Test
+	public void updateCurriculumElement_moveToOtherCurriculum()
+	throws IOException, URISyntaxException {
+		RestConnection conn = new RestConnection();
+		assertTrue(conn.login("administrator", "openolat"));
+
+		Organisation defOrganisation = organisationService.getDefaultOrganisation();
+		Organisation organisation = organisationService.createOrganisation("REST Parent Organisation 25", "REST-p-25-organisation", "", defOrganisation, null);
+		Curriculum sourceCurriculum = curriculumService.createCurriculum("REST-Curriculum-elements", "REST Source Curriculum", "A source curriculum", organisation);
+		Curriculum targetCurriculum = curriculumService.createCurriculum("REST-Curriculum-elements", "REST Target Curriculum", "A target curriculum", organisation);
+		CurriculumElement element1 = curriculumService.createCurriculumElement("Element-25", "Element2 5", CurriculumElementStatus.active,
+				null, null, null, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, sourceCurriculum);
+		CurriculumElement element1_1 = curriculumService.createCurriculumElement("Element-25-1", "Element 25-1", CurriculumElementStatus.active,
+				null, null, element1, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, sourceCurriculum);
+		
+		dbInstance.commitAndCloseSession();
+		
+		CurriculumElementVO vo = new CurriculumElementVO();
+		vo.setKey(element1.getKey());
+		vo.setDisplayName("REST updated element");
+		vo.setCurriculumKey(targetCurriculum.getKey());
+
+		URI request = UriBuilder.fromUri(getContextURI()).path("curriculum").path(targetCurriculum.getKey().toString())
+				.path("elements").build();
+		HttpPut method = conn.createPut(request, MediaType.APPLICATION_JSON, true);
+		conn.addJsonEntity(method, vo);
+		
+		HttpResponse response = conn.execute(method);
+		Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+		
+		// checked VO
+		CurriculumElementVO savedVo = conn.parse(response, CurriculumElementVO.class);
+		Assert.assertNotNull(savedVo);
+		Assert.assertNotNull(savedVo.getKey());
+		Assert.assertEquals("REST updated element", savedVo.getDisplayName());
+		Assert.assertEquals(targetCurriculum.getKey(), savedVo.getCurriculumKey());
+		Assert.assertNull(savedVo.getParentElementKey());
+		
+		// checked database
+		CurriculumElement savedElement = curriculumService.getCurriculumElement(new CurriculumElementRefImpl(savedVo.getKey()));
+		Assert.assertNotNull(savedElement);
+		Assert.assertEquals(savedVo.getKey(), savedElement.getKey());
+		Assert.assertEquals("REST updated element", savedElement.getDisplayName());
+		Assert.assertEquals(targetCurriculum, savedElement.getCurriculum());
+
+		// check the source curriculum (low level to make sure the position are rights)
+		CurriculumImpl reloadedCurriculum = (CurriculumImpl)curriculumDao.loadByKey(sourceCurriculum.getKey());
+		List<CurriculumElement> rootElements = reloadedCurriculum.getRootElements();
+		Assert.assertTrue(rootElements.isEmpty());
+
+		// check the target curriculum (low level to make sure the position are rights)
+		CurriculumImpl reloadedTargetCurriculum = (CurriculumImpl)curriculumDao.loadByKey(targetCurriculum.getKey());
+		List<CurriculumElement> targetRootElements = reloadedTargetCurriculum.getRootElements();
+		Assert.assertEquals(1, targetRootElements.size());
+		Assert.assertTrue(targetRootElements.contains(element1));
+		
+		List<CurriculumElement> movedElements = curriculumElementDao.loadElements(reloadedTargetCurriculum, CurriculumElementStatus.values());
+		Assert.assertTrue(movedElements.contains(element1));
+		Assert.assertTrue(movedElements.contains(element1_1));
+		
+		List<CurriculumElement> sourceElements = curriculumElementDao.loadElements(sourceCurriculum, CurriculumElementStatus.values());
+		Assert.assertTrue(sourceElements.isEmpty());
+	}
+	
 	/**
 	 * Set the parent element key as itself.
 	 * 
-- 
GitLab