From 49592e41fdf2330be5c9920dd7ad3e2225e7ba39 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Thu, 15 Feb 2018 11:51:12 +0100
Subject: [PATCH] OO-3312: hide the lost+found in the document pool site,
 exclude competence gained from a taxonomy level with the identifier
 lost+found or orphan-fach (inclusive parent line) to access the document pool
 site

---
 .../modules/docpool/DocumentPoolManager.java  |  4 +-
 .../manager/DocumentPoolManagerImpl.java      | 16 +++--
 .../docpool/site/DocumentPoolSite.java        |  4 +-
 .../olat/modules/taxonomy/TaxonomyModule.java | 10 +++
 .../taxonomy/manager/TaxonomyTreeBuilder.java | 39 +++++++++---
 .../taxonomy/model/TaxonomyTreeNode.java      |  6 +-
 .../manager/DocumentPoolManagerTest.java      | 62 ++++++++++++++++---
 7 files changed, 111 insertions(+), 30 deletions(-)

diff --git a/src/main/java/org/olat/modules/docpool/DocumentPoolManager.java b/src/main/java/org/olat/modules/docpool/DocumentPoolManager.java
index 2cd06ef7568..dc90d7002d8 100644
--- a/src/main/java/org/olat/modules/docpool/DocumentPoolManager.java
+++ b/src/main/java/org/olat/modules/docpool/DocumentPoolManager.java
@@ -30,7 +30,9 @@ import org.olat.basesecurity.IdentityRef;
 public interface DocumentPoolManager {
 	
 	/**
-	 * Has a valid competence to see the document pool.
+	 * Has a valid competence to see the document pool. The presence of a
+	 * taxonomy level with the identifier "lost+found" or "orphan-fach" in the
+	 * parent line of level exclude the competence.
 	 * 
 	 * @param identity The identity which want to access the document pool
 	 * @return true/false
diff --git a/src/main/java/org/olat/modules/docpool/manager/DocumentPoolManagerImpl.java b/src/main/java/org/olat/modules/docpool/manager/DocumentPoolManagerImpl.java
index 6c6f681b589..b39a4325b02 100644
--- a/src/main/java/org/olat/modules/docpool/manager/DocumentPoolManagerImpl.java
+++ b/src/main/java/org/olat/modules/docpool/manager/DocumentPoolManagerImpl.java
@@ -29,6 +29,7 @@ import org.olat.core.util.StringHelper;
 import org.olat.modules.docpool.DocumentPoolManager;
 import org.olat.modules.docpool.DocumentPoolModule;
 import org.olat.modules.taxonomy.TaxonomyCompetenceTypes;
+import org.olat.modules.taxonomy.TaxonomyModule;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -44,6 +45,8 @@ public class DocumentPoolManagerImpl implements DocumentPoolManager {
 	@Autowired
 	private DB dbInstance;
 	@Autowired
+	private TaxonomyModule taxonomyModule;
+	@Autowired
 	private DocumentPoolModule documentPoolModule;
 
 	@Override
@@ -56,9 +59,9 @@ public class DocumentPoolManagerImpl implements DocumentPoolManager {
 		StringBuilder sb = new StringBuilder(256);
 		sb.append("select competence.key from ctaxonomycompetence competence")
 		  .append(" inner join competence.identity ident")
-		  .append(" inner join competence.taxonomyLevel taxonomyLevel")
-		  .append(" inner join taxonomyLevel.type type")
-		  .append(" where taxonomyLevel.taxonomy.key=:taxonomyKey and ident.key=:identityKey")
+		  .append(" inner join competence.taxonomyLevel level")
+		  .append(" inner join level.type type")
+		  .append(" where level.taxonomy.key=:taxonomyKey and ident.key=:identityKey")
 		  .append(" and (")
 		  .append("  (competence.type='").append(TaxonomyCompetenceTypes.manage.name()).append("' and type.documentsLibraryManagerCompetenceEnabled=true)")
 		  .append("  or")
@@ -67,13 +70,18 @@ public class DocumentPoolManagerImpl implements DocumentPoolManager {
 		  .append("  (competence.type='").append(TaxonomyCompetenceTypes.have.name()).append("' and type.documentsLibraryHaveCompetenceReadEnabled=true)")
 		  .append("  or")
 		  .append("  (competence.type='").append(TaxonomyCompetenceTypes.target.name()).append("' and type.documentsLibraryTargetCompetenceReadEnabled=true)")
-		  .append(")");
+		  .append(" ) and not(level.identifier in (:lostFoundIds))")
+		  .append(" and not exists (select parent.key from ctaxonomylevel parent")
+		  .append("   where locate(parent.materializedPathKeys, level.materializedPathKeys) = 1")
+		  .append("   and parent.identifier in (:lostFoundIds)")
+		  .append(" )");
 		
 		List<Long> competenceKeys = dbInstance.getCurrentEntityManager()
 			.createQuery(sb.toString(), Long.class)
 			.setFlushMode(FlushModeType.COMMIT)//don't flush for this query
 			.setParameter("taxonomyKey", new Long(taxonomyKey))
 			.setParameter("identityKey", identity.getKey())
+			.setParameter("lostFoundIds", taxonomyModule.getLostAndFoundsIdentifiers())
 			.setFirstResult(0)
 			.setMaxResults(1)
 			.getResultList();
diff --git a/src/main/java/org/olat/modules/docpool/site/DocumentPoolSite.java b/src/main/java/org/olat/modules/docpool/site/DocumentPoolSite.java
index 776181fb77c..a4efe6e1a3b 100644
--- a/src/main/java/org/olat/modules/docpool/site/DocumentPoolSite.java
+++ b/src/main/java/org/olat/modules/docpool/site/DocumentPoolSite.java
@@ -70,13 +70,11 @@ public class DocumentPoolSite extends AbstractSiteInstance {
 		OLATResourceable ores = OresHelper.createOLATResourceableInstance(DocumentPoolSite.class, 0l);
 		ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
 		WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ureq, ores, new StateSite(this), wControl, true);
-		DocumentPoolMainController mainController = new DocumentPoolMainController(ureq, bwControl);
-		return mainController;
+		return new DocumentPoolMainController(ureq, bwControl);
 	}
 
 	@Override
 	public void reset() {
 		curNavElem = new DefaultNavElement(origNavElem);
 	}
-
 }
diff --git a/src/main/java/org/olat/modules/taxonomy/TaxonomyModule.java b/src/main/java/org/olat/modules/taxonomy/TaxonomyModule.java
index 22684a4b3dc..04f8b1aca41 100644
--- a/src/main/java/org/olat/modules/taxonomy/TaxonomyModule.java
+++ b/src/main/java/org/olat/modules/taxonomy/TaxonomyModule.java
@@ -19,6 +19,9 @@
  */
 package org.olat.modules.taxonomy;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.olat.core.configuration.AbstractSpringModule;
 import org.olat.core.configuration.ConfigOnOff;
 import org.olat.core.util.StringHelper;
@@ -72,4 +75,11 @@ public class TaxonomyModule extends AbstractSpringModule implements ConfigOnOff
 	public boolean isManagedTaxonomyLevels() {
 		return true;
 	}
+	
+	public List<String> getLostAndFoundsIdentifiers() {
+		List<String> identifiers = new ArrayList<>(2);
+		identifiers.add("orphan-fach");
+		identifiers.add("lost+found");
+		return identifiers;
+	}
 }
diff --git a/src/main/java/org/olat/modules/taxonomy/manager/TaxonomyTreeBuilder.java b/src/main/java/org/olat/modules/taxonomy/manager/TaxonomyTreeBuilder.java
index 655d2b08e5b..b458740aa40 100644
--- a/src/main/java/org/olat/modules/taxonomy/manager/TaxonomyTreeBuilder.java
+++ b/src/main/java/org/olat/modules/taxonomy/manager/TaxonomyTreeBuilder.java
@@ -39,6 +39,7 @@ import org.olat.modules.taxonomy.TaxonomyCompetence;
 import org.olat.modules.taxonomy.TaxonomyCompetenceTypes;
 import org.olat.modules.taxonomy.TaxonomyLevel;
 import org.olat.modules.taxonomy.TaxonomyLevelType;
+import org.olat.modules.taxonomy.TaxonomyModule;
 import org.olat.modules.taxonomy.TaxonomyService;
 import org.olat.modules.taxonomy.model.TaxonomyTreeNode;
 import org.olat.modules.taxonomy.model.TaxonomyTreeNodeType;
@@ -63,10 +64,12 @@ public class TaxonomyTreeBuilder {
 	private final boolean enableTemplates;
 	
 	private final TaxonomyService taxonomyService;
+	private final List<String> lostAndFoundIdentifiers;
 	
 	public TaxonomyTreeBuilder(Taxonomy taxonomy, Identity identity, String rootTitle,
 			boolean isTaxonomyAdmin, boolean enableTemplates, String templateDirectory, Locale locale) {
 		taxonomyService = CoreSpringFactory.getImpl(TaxonomyService.class);
+		lostAndFoundIdentifiers = CoreSpringFactory.getImpl(TaxonomyModule.class).getLostAndFoundsIdentifiers();
 		this.locale = locale;
 		this.taxonomy = taxonomy;
 		this.identity = identity;
@@ -113,7 +116,7 @@ public class TaxonomyTreeBuilder {
 				Long key = taxonomyLevel.getKey();
 				TaxonomyTreeNode node = fieldKeyToNode.get(key);
 				if(node == null) {
-					node = new TaxonomyTreeNode(taxonomy, taxonomyLevel);
+					node = new TaxonomyTreeNode(taxonomy, taxonomyLevel, getType(taxonomyLevel));
 					TaxonomyLevelType type = taxonomyLevel.getType();
 					if(type != null && StringHelper.containsNonWhitespace(type.getCssClass())) {
 						node.setIconCssClass(type.getCssClass());
@@ -130,7 +133,7 @@ public class TaxonomyTreeBuilder {
 					TaxonomyTreeNode parentNode = fieldKeyToNode.get(parentKey);
 					if(parentNode == null) {
 						parentLevel = keytoLevels.get(parentKey);//to use the fetched type
-						parentNode = new TaxonomyTreeNode(taxonomy, parentLevel);
+						parentNode = new TaxonomyTreeNode(taxonomy, parentLevel, getType(parentLevel));
 						TaxonomyLevelType type = parentLevel.getType();
 						if(type != null && StringHelper.containsNonWhitespace(type.getCssClass())) {
 							parentNode.setIconCssClass(type.getCssClass());
@@ -148,6 +151,21 @@ public class TaxonomyTreeBuilder {
 		return gtm;
 	}
 	
+	private TaxonomyTreeNodeType getType(TaxonomyLevel taxonomyLevel) {
+		TaxonomyTreeNodeType type;
+		String identifier = taxonomyLevel.getIdentifier();
+		if(StringHelper.containsNonWhitespace(identifier)) {
+			if(lostAndFoundIdentifiers.contains(identifier)) {
+				type = TaxonomyTreeNodeType.lostAndFound;
+			} else {
+				type = TaxonomyTreeNodeType.taxonomyLevel;
+			}
+		} else {
+			type = TaxonomyTreeNodeType.taxonomyLevel;
+		}
+		return type;		
+	}
+	
 	private void sort(TaxonomyTreeNode parent) {
 		parent.sort(new TaxonomyTreeNodeComparator());
 		for(int i=parent.getChildCount(); i-->0; ) {
@@ -160,9 +178,9 @@ public class TaxonomyTreeBuilder {
 		do {
 			someInvisible = false;
 			List<TaxonomyTreeNode> children = listChildren(parent);
-			
 			for(TaxonomyTreeNode child:children) {
-				if(!child.isVisible())  {
+				//remove invisible nodes and lost+found
+				if(!child.isVisible() || child.getType() == TaxonomyTreeNodeType.lostAndFound)  {
 					List<TaxonomyTreeNode> childrenOfChild = listChildren(child);
 					parent.remove(child);
 					for(TaxonomyTreeNode childOfChild : childrenOfChild) {
@@ -206,7 +224,13 @@ public class TaxonomyTreeBuilder {
 	private void computePermissionsRecursive(TaxonomyTreeNode node, Map<TaxonomyLevel, List<TaxonomyCompetenceTypes>> levelToCompetences) {
 		boolean hasRead = node.isCanRead();
 		boolean hasWrite = node.isCanWrite();
-		if(node.getTaxonomyLevel() != null) {
+		
+		if(node.getType() == TaxonomyTreeNodeType.lostAndFound) {
+			hasRead = isTaxonomyAdmin;
+			hasWrite = isTaxonomyAdmin;
+			node.setCanRead(hasRead);
+			node.setCanWrite(hasWrite);
+		} else if(node.getTaxonomyLevel() != null) {
 			TaxonomyLevel level = node.getTaxonomyLevel();
 			TaxonomyLevelType type = level.getType();
 			if(type != null) {
@@ -238,11 +262,6 @@ public class TaxonomyTreeBuilder {
 			hasWrite = isTaxonomyAdmin;
 			node.setCanRead(hasRead);
 			node.setCanWrite(hasWrite);
-		} else if(node.getType() == TaxonomyTreeNodeType.lostAndFound) {
-			hasRead = isTaxonomyAdmin;
-			hasWrite = false;
-			node.setCanRead(hasRead);
-			node.setCanWrite(hasWrite);
 		}
 		
 		for(int i=node.getChildCount(); i-->0; ) {
diff --git a/src/main/java/org/olat/modules/taxonomy/model/TaxonomyTreeNode.java b/src/main/java/org/olat/modules/taxonomy/model/TaxonomyTreeNode.java
index 3823c037ab7..12b0a77c45d 100644
--- a/src/main/java/org/olat/modules/taxonomy/model/TaxonomyTreeNode.java
+++ b/src/main/java/org/olat/modules/taxonomy/model/TaxonomyTreeNode.java
@@ -57,13 +57,13 @@ public class TaxonomyTreeNode extends GenericTreeNode {
 		nodeType = type;
 	}
 	
-	public TaxonomyTreeNode(Taxonomy taxonomy, TaxonomyLevel taxonomyLevel) {
+	public TaxonomyTreeNode(Taxonomy taxonomy, TaxonomyLevel taxonomyLevel, TaxonomyTreeNodeType type) {
 		super(taxonomyLevel.getKey().toString());
 		setTitle(taxonomyLevel.getDisplayName());
 		this.taxonomy = taxonomy;
 		this.taxonomyLevel = taxonomyLevel;
 		setUserObject(taxonomyLevel);
-		nodeType = TaxonomyTreeNodeType.taxonomyLevel;
+		nodeType = type;
 	}
 	
 	public TaxonomyTreeNodeType getType() {
@@ -72,7 +72,7 @@ public class TaxonomyTreeNode extends GenericTreeNode {
 	
 	public boolean isVisible() {
 		return nodeType == TaxonomyTreeNodeType.taxonomy 
-				|| nodeType == TaxonomyTreeNodeType.templates 
+				|| nodeType == TaxonomyTreeNodeType.templates
 				|| (taxonomyLevel != null && (taxonomyLevel.getType() == null ? true : taxonomyLevel.getType().isVisible()));
 	}
 	
diff --git a/src/test/java/org/olat/modules/docpool/manager/DocumentPoolManagerTest.java b/src/test/java/org/olat/modules/docpool/manager/DocumentPoolManagerTest.java
index 0eda1be36e0..cba262187f7 100644
--- a/src/test/java/org/olat/modules/docpool/manager/DocumentPoolManagerTest.java
+++ b/src/test/java/org/olat/modules/docpool/manager/DocumentPoolManagerTest.java
@@ -84,7 +84,7 @@ public class DocumentPoolManagerTest extends OlatTestCase {
 	public void hasCompetenceByTaxonomy_manage() {
 		// create a level and competence with a type teach
 		Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("competent-8");
-		TaxonomyLevelType type = createTypeLevelCompetence(id, TaxonomyCompetenceTypes.manage);
+		TaxonomyLevelType type = createTypeLevelCompetence(id, null, TaxonomyCompetenceTypes.manage);
 		// set read for teach competence
 		type.setDocumentsLibraryManageCompetenceEnabled(true);
 		type = taxonomyLevelTypeDao.updateTaxonomyLevelType(type);
@@ -98,7 +98,7 @@ public class DocumentPoolManagerTest extends OlatTestCase {
 	public void hasCompetenceByTaxonomy_teachRead() {
 		// create a level and competence with a type teach
 		Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("competent-8");
-		TaxonomyLevelType type = createTypeLevelCompetence(id, TaxonomyCompetenceTypes.teach);
+		TaxonomyLevelType type = createTypeLevelCompetence(id, null, TaxonomyCompetenceTypes.teach);
 		// set read for teach competence
 		type.setDocumentsLibraryTeachCompetenceReadEnabled(true);
 		type = taxonomyLevelTypeDao.updateTaxonomyLevelType(type);
@@ -112,7 +112,7 @@ public class DocumentPoolManagerTest extends OlatTestCase {
 	public void hasCompetenceByTaxonomy_teachWrite() {
 		// create a level and competence with a type teach
 		Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("competent-8");
-		TaxonomyLevelType type = createTypeLevelCompetence(id, TaxonomyCompetenceTypes.teach);
+		TaxonomyLevelType type = createTypeLevelCompetence(id, null, TaxonomyCompetenceTypes.teach);
 		// set read for teach competence
 		type.setDocumentsLibraryTeachCompetenceWriteEnabled(true);
 		type = taxonomyLevelTypeDao.updateTaxonomyLevelType(type);
@@ -126,7 +126,7 @@ public class DocumentPoolManagerTest extends OlatTestCase {
 	public void hasCompetenceByTaxonomy_teach_negative() {
 		// create a level and competence with a type teach
 		Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("competent-8");
-		TaxonomyLevelType type = createTypeLevelCompetence(id, TaxonomyCompetenceTypes.teach);
+		TaxonomyLevelType type = createTypeLevelCompetence(id, null, TaxonomyCompetenceTypes.teach);
 		// set read for teach competence
 		type.setDocumentsLibraryManageCompetenceEnabled(true);
 		type.setDocumentsLibraryHaveCompetenceReadEnabled(true);
@@ -142,7 +142,7 @@ public class DocumentPoolManagerTest extends OlatTestCase {
 	public void hasCompetenceByTaxonomy_have() {
 		// create a level and competence with a type teach
 		Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("competent-8");
-		TaxonomyLevelType type = createTypeLevelCompetence(id, TaxonomyCompetenceTypes.have);
+		TaxonomyLevelType type = createTypeLevelCompetence(id, null, TaxonomyCompetenceTypes.have);
 		// set read for teach competence
 		type.setDocumentsLibraryHaveCompetenceReadEnabled(true);
 		type = taxonomyLevelTypeDao.updateTaxonomyLevelType(type);
@@ -156,7 +156,7 @@ public class DocumentPoolManagerTest extends OlatTestCase {
 	public void hasCompetenceByTaxonomy_have_negative() {
 		// create a level and competence with a type teach
 		Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("competent-8");
-		TaxonomyLevelType type = createTypeLevelCompetence(id, TaxonomyCompetenceTypes.have);
+		TaxonomyLevelType type = createTypeLevelCompetence(id, null, TaxonomyCompetenceTypes.have);
 		// set read for teach competence
 		type.setDocumentsLibraryTeachCompetenceReadEnabled(true);
 		type = taxonomyLevelTypeDao.updateTaxonomyLevelType(type);
@@ -170,7 +170,7 @@ public class DocumentPoolManagerTest extends OlatTestCase {
 	public void hasCompetenceByTaxonomy_target() {
 		// create a level and competence with a type teach
 		Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("competent-8");
-		TaxonomyLevelType type = createTypeLevelCompetence(id, TaxonomyCompetenceTypes.target);
+		TaxonomyLevelType type = createTypeLevelCompetence(id, null, TaxonomyCompetenceTypes.target);
 		// set read for teach competence
 		type.setDocumentsLibraryTargetCompetenceReadEnabled(true);
 		type = taxonomyLevelTypeDao.updateTaxonomyLevelType(type);
@@ -193,11 +193,55 @@ public class DocumentPoolManagerTest extends OlatTestCase {
 		Assert.assertFalse(hasCompetence);
 	}
 	
-	private TaxonomyLevelType createTypeLevelCompetence(Identity id, TaxonomyCompetenceTypes competenceType) {
+	/**
+	 * The test check if the presence of a level with the identifier lost+found
+	 * in the parent line stop the competence.
+	 */
+	@Test
+	public void lostAndFoundSpecialCase() {
+		Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("competent-8");
+		Taxonomy taxonomy = getDocumentPoolTaxonomy();
+		TaxonomyLevel level1 = taxonomyLevelDao.createTaxonomyLevel("DP-Lev. " + UUID.randomUUID(), "Competence level", "A competence", null, null, null, null, taxonomy);
+		TaxonomyLevel level2 = taxonomyLevelDao.createTaxonomyLevel("DP-Lev. " + UUID.randomUUID(), "Competence level", "A competence", null, null, level1, null, taxonomy);
+		TaxonomyLevelType type = createTypeLevelCompetence(id, level2, TaxonomyCompetenceTypes.have);
+		type.setDocumentsLibraryHaveCompetenceReadEnabled(true);
+		type = taxonomyLevelTypeDao.updateTaxonomyLevelType(type);
+		dbInstance.commitAndCloseSession();
+		
+		//check first without lost+found
+		boolean hasCompetence = documentPoolManager.hasValidCompetence(id);
+		Assert.assertTrue(hasCompetence);
+		
+		//change the identifier level 2
+		level2.setIdentifier("lost+found");
+		level2 = taxonomyLevelDao.updateTaxonomyLevel(level2);
+		dbInstance.commitAndCloseSession();
+		
+		//access refused
+		boolean hasLostCompetence = documentPoolManager.hasValidCompetence(id);
+		Assert.assertFalse(hasLostCompetence);
+		
+		// remove lost + found
+		level2.setIdentifier("DP-Lev. " + UUID.randomUUID());
+		level2 = taxonomyLevelDao.updateTaxonomyLevel(level2);
+		dbInstance.commitAndCloseSession();
+		// access allowed
+		boolean hasAgainCompetence = documentPoolManager.hasValidCompetence(id);
+		Assert.assertTrue(hasAgainCompetence);
+		
+		//root will be lost
+		level1.setIdentifier("lost+found");
+		level1 = taxonomyLevelDao.updateTaxonomyLevel(level1);
+		dbInstance.commitAndCloseSession();
+		boolean hasLostAgainCompetence = documentPoolManager.hasValidCompetence(id);
+		Assert.assertFalse(hasLostAgainCompetence);
+	}
+	
+	private TaxonomyLevelType createTypeLevelCompetence(Identity id, TaxonomyLevel parent, TaxonomyCompetenceTypes competenceType) {
 		String levelId = "DP-Lev. " + UUID.randomUUID();
 		Taxonomy taxonomy = getDocumentPoolTaxonomy();
 		TaxonomyLevelType type = taxonomyLevelTypeDao.createTaxonomyLevelType("Type-docpool", "A type for document pool", "Typed", "TYP-0", taxonomy);
-		TaxonomyLevel level = taxonomyLevelDao.createTaxonomyLevel(levelId, "Competence level", "A competence", null, null, null, type, taxonomy);
+		TaxonomyLevel level = taxonomyLevelDao.createTaxonomyLevel(levelId, "Competence level", "A competence", null, null, parent, type, taxonomy);
 		TaxonomyCompetence competenceTarget = taxonomyCompetenceDao.createTaxonomyCompetence(competenceType, level, id, null);
 		dbInstance.commit();
 		type.setDocumentsLibraryManageCompetenceEnabled(false);
-- 
GitLab