From c25464a506327163ede0f31210ba0729b18f450e Mon Sep 17 00:00:00 2001
From: uhensler <none@none>
Date: Wed, 10 Jan 2018 08:04:16 +0100
Subject: [PATCH] OO-3113: Configuration of the pool manager rights

---
 .../modules/qpool/QPoolSecurityCallback.java  |  18 ++-
 .../qpool/QuestionItemSecurityCallback.java   |   2 +
 .../modules/qpool/QuestionPoolModule.java     | 131 ++++++++++++++++++
 .../security/ProcesslessSecurityCallback.java |  61 ++++++--
 .../QPoolSecurityCallbackFactory.java         |  16 +--
 ...ck.java => QPoolSecurityCallbackImpl.java} |  50 ++++++-
 .../RegularQPoolSecurityCallback.java         |  66 ---------
 .../ReviewProcessSecurityCallback.java        |  35 +++--
 .../qpool/ui/AbstractItemListController.java  |   9 +-
 .../qpool/ui/_content/item_list_overview.html |   2 +-
 .../qpool/ui/_i18n/LocalStrings_de.properties |   1 +
 .../qpool/ui/_i18n/LocalStrings_en.properties |   1 +
 ...stionPoolAdminConfigurationController.java |  88 ++++++++++--
 .../qpool/ui/admin/_content/admin_config.html |   3 +
 .../ui/admin/_i18n/LocalStrings_de.properties |  10 ++
 .../ui/admin/_i18n/LocalStrings_en.properties |  10 ++
 .../ui/metadata/ExtendedSearchController.java |   1 +
 .../ui/tree/QuestionPoolMenuTreeModel.java    |  54 +++++---
 .../ui/tree/ReviewProcessAdminTreeNode.java   |  59 ++++++++
 19 files changed, 477 insertions(+), 140 deletions(-)
 rename src/main/java/org/olat/modules/qpool/security/{AdministratorQPoolSecurityCallback.java => QPoolSecurityCallbackImpl.java} (57%)
 delete mode 100644 src/main/java/org/olat/modules/qpool/security/RegularQPoolSecurityCallback.java
 create mode 100644 src/main/java/org/olat/modules/qpool/ui/admin/_content/admin_config.html
 create mode 100644 src/main/java/org/olat/modules/qpool/ui/tree/ReviewProcessAdminTreeNode.java

diff --git a/src/main/java/org/olat/modules/qpool/QPoolSecurityCallback.java b/src/main/java/org/olat/modules/qpool/QPoolSecurityCallback.java
index 7072a7f0a2a..4bae90c1f72 100644
--- a/src/main/java/org/olat/modules/qpool/QPoolSecurityCallback.java
+++ b/src/main/java/org/olat/modules/qpool/QPoolSecurityCallback.java
@@ -26,6 +26,10 @@ package org.olat.modules.qpool;
  *
  */
 public interface QPoolSecurityCallback {
+	
+	public void setAdmin(boolean admin);
+	
+	public void setPoolAdmin(boolean poolAdmin);
 
 	boolean canUseCollections();
 
@@ -35,6 +39,18 @@ public interface QPoolSecurityCallback {
 
 	boolean canUseReviewProcess();
 
-	boolean canAdmin();
+	boolean canEditAllQuestions();
+	
+	boolean canConfigReviewProcess();
+	
+	boolean canConfigTaxonomies();
+	
+	boolean canConfigPools();
+	
+	boolean canConfigItemTypes();
+	
+	boolean canConfigEducationalContext();
+	
+	boolean canConfigLicences();
 
 }
diff --git a/src/main/java/org/olat/modules/qpool/QuestionItemSecurityCallback.java b/src/main/java/org/olat/modules/qpool/QuestionItemSecurityCallback.java
index 2454fd8c64c..4771c2c6007 100644
--- a/src/main/java/org/olat/modules/qpool/QuestionItemSecurityCallback.java
+++ b/src/main/java/org/olat/modules/qpool/QuestionItemSecurityCallback.java
@@ -35,6 +35,8 @@ public interface QuestionItemSecurityCallback {
 	
 	public void setAdmin(boolean admin);
 	
+	public void setPoolAdmin(boolean poolAdmin);
+	
 	public boolean canEditQuestion();
 	
 	public boolean canEditMetadata();
diff --git a/src/main/java/org/olat/modules/qpool/QuestionPoolModule.java b/src/main/java/org/olat/modules/qpool/QuestionPoolModule.java
index 6e546128f50..7acd6c6f342 100644
--- a/src/main/java/org/olat/modules/qpool/QuestionPoolModule.java
+++ b/src/main/java/org/olat/modules/qpool/QuestionPoolModule.java
@@ -61,6 +61,14 @@ public class QuestionPoolModule extends AbstractSpringModule implements ConfigOn
 	private static final String FINAL_VISIBLE_TEACH = "final.visible.teach";
 	private static final String TAXONOMY_QPOOL_KEY = "taxonomy.qpool.key";
 	public static final String DEFAULT_TAXONOMY_QPOOL_IDENTIFIER = "QPOOL";
+	private static final String POOL_ADMIN_METADATA = "pool.admin.metadata";
+	private static final String POOL_ADMIN_STATUS = "pool.admin.status";
+	private static final String POOL_ADMIN_REVIEW_PROCESS = "pool.admin.review.process";
+	private static final String POOL_ADMIN_TAXONOMY = "pool.admin.taxonomy";
+	private static final String POOL_ADMIN_POOLS = "pool.admin.pools";
+	private static final String POOL_ADMIN_ITEM_TYPES = "pool.admin.item.types";
+	private static final String POOL_ADMIN_EDUCATIONAL_CONTEXT = "pool.admin.educational.context";
+	private static final String POOL_ADMIN_LICENSES = "pool.admin.licenses";
 	
 	private boolean collectionsEnabled = true;
 	private boolean poolsEnabled = true;
@@ -70,6 +78,15 @@ public class QuestionPoolModule extends AbstractSpringModule implements ConfigOn
 	private boolean finalVisibleTeach = false;
 	private String taxonomyQPoolKey;
 	
+	private boolean poolAdminAllowedToEditMetadata = false;
+	private boolean poolAdminAllowedToEditStatus = false;
+	private boolean poolAdminAllowedToConfigReviewProcess = true;
+	private boolean poolAdminAllowedToConfigTaxonomy = true;
+	private boolean poolAdminAllowedToConfigPools = true;
+	private boolean poolAdminAllowedToConfigItemTypes = true;
+	private boolean poolAdminAllowedToConfigEducationalContext = true;
+	private boolean poolAdminAllowedToConfigLicenses = true;
+	
 	@Autowired
 	private DB dbInstance;
 	@Autowired
@@ -154,6 +171,47 @@ public class QuestionPoolModule extends AbstractSpringModule implements ConfigOn
 		if(StringHelper.containsNonWhitespace(taxonomyQPoolKeyObj)) {
 			taxonomyQPoolKey = taxonomyQPoolKeyObj;
 		}
+		
+		String poolAdminAllowedToEditMetadataObj = getStringPropertyValue(POOL_ADMIN_METADATA, true);
+		if(StringHelper.containsNonWhitespace(poolAdminAllowedToEditMetadataObj)) {
+			poolAdminAllowedToEditMetadata = "true".equals(poolAdminAllowedToEditMetadataObj);
+		}
+		
+		String poolAdminAllowedToEditStatusObj = getStringPropertyValue(POOL_ADMIN_STATUS, true);
+		if(StringHelper.containsNonWhitespace(poolAdminAllowedToEditStatusObj)) {
+			poolAdminAllowedToEditStatus = "true".equals(poolAdminAllowedToEditStatusObj);
+		}
+		
+		String poolAdminAllowedToConfigReviewProcessObj = getStringPropertyValue(POOL_ADMIN_REVIEW_PROCESS, true);
+		if(StringHelper.containsNonWhitespace(poolAdminAllowedToConfigReviewProcessObj)) {
+			poolAdminAllowedToConfigReviewProcess = "true".equals(poolAdminAllowedToConfigReviewProcessObj);
+		}
+		
+		String poolAdminAllowedToConfigTaxonomyObj = getStringPropertyValue(POOL_ADMIN_TAXONOMY, true);
+		if(StringHelper.containsNonWhitespace(poolAdminAllowedToConfigTaxonomyObj)) {
+			poolAdminAllowedToConfigTaxonomy = "true".equals(poolAdminAllowedToConfigTaxonomyObj);
+		}
+		
+		String poolAdminAllowedToConfigPoolsObj = getStringPropertyValue(POOL_ADMIN_POOLS, true);
+		if(StringHelper.containsNonWhitespace(poolAdminAllowedToConfigPoolsObj)) {
+			poolAdminAllowedToConfigPools = "true".equals(poolAdminAllowedToConfigPoolsObj);
+		}
+		
+		String poolAdminAllowedToConfigItemTypesObj = getStringPropertyValue(POOL_ADMIN_ITEM_TYPES, true);
+		if(StringHelper.containsNonWhitespace(poolAdminAllowedToConfigItemTypesObj)) {
+			poolAdminAllowedToConfigItemTypes = "true".equals(poolAdminAllowedToConfigItemTypesObj);
+		}
+		
+		String poolAdminAllowedToConfigEducationalContextObj = getStringPropertyValue(POOL_ADMIN_EDUCATIONAL_CONTEXT, true);
+		if(StringHelper.containsNonWhitespace(poolAdminAllowedToConfigEducationalContextObj)) {
+			poolAdminAllowedToConfigEducationalContext = "true".equals(poolAdminAllowedToConfigEducationalContextObj);
+		}
+		
+		String poolAdminAllowedToConfigLicensesObj = getStringPropertyValue(POOL_ADMIN_LICENSES, true);
+		if(StringHelper.containsNonWhitespace(poolAdminAllowedToConfigLicensesObj)) {
+			poolAdminAllowedToConfigLicenses = "true".equals(poolAdminAllowedToConfigLicensesObj);
+		}
+		
 	}
 
 	@Override
@@ -270,4 +328,77 @@ public class QuestionPoolModule extends AbstractSpringModule implements ConfigOn
 		setStringProperty(TAXONOMY_QPOOL_KEY, taxonomyQPoolKey, true);
 	}
 
+	public boolean isPoolAdminAllowedToEditMetadata() {
+		return poolAdminAllowedToEditMetadata;
+	}
+
+	public void setPoolAdminAllowedToEditMetadata(boolean poolAdminAllowedToEditMetadata) {
+		this.poolAdminAllowedToEditMetadata = poolAdminAllowedToEditMetadata;
+		setStringProperty(POOL_ADMIN_METADATA, Boolean.toString(poolAdminAllowedToEditMetadata), true);
+	}
+
+	public boolean isPoolAdminAllowedToEditStatus() {
+		return poolAdminAllowedToEditStatus;
+	}
+
+	public void setPoolAdminAllowedToEditStatus(boolean poolAdminAllowedToEditStatus) {
+		this.poolAdminAllowedToEditStatus = poolAdminAllowedToEditStatus;
+		setStringProperty(POOL_ADMIN_STATUS, Boolean.toString(poolAdminAllowedToEditStatus), true);
+	}
+
+
+	public boolean isPoolAdminAllowedToConfigReviewProcess() {
+		return poolAdminAllowedToConfigReviewProcess;
+	}
+
+	public void setPoolAdminAllowedToConfigReviewProcess(boolean poolAdminAllowedToConfigReviewProcess) {
+		this.poolAdminAllowedToConfigReviewProcess = poolAdminAllowedToConfigReviewProcess;
+		setStringProperty(POOL_ADMIN_REVIEW_PROCESS, Boolean.toString(poolAdminAllowedToConfigReviewProcess), true);
+	}
+
+	public boolean isPoolAdminAllowedToConfigTaxonomy() {
+		return poolAdminAllowedToConfigTaxonomy;
+	}
+
+	public void setPoolAdminAllowedToConfigTaxonomy(boolean poolAdminAllowedToConfigTaxonomy) {
+		this.poolAdminAllowedToConfigTaxonomy = poolAdminAllowedToConfigTaxonomy;
+		setStringProperty(POOL_ADMIN_TAXONOMY, Boolean.toString(poolAdminAllowedToConfigTaxonomy), true);
+	}
+
+	public boolean isPoolAdminAllowedToConfigPools() {
+		return poolAdminAllowedToConfigPools;
+	}
+
+	public void setPoolAdminAllowedToConfigPools(boolean poolAdminAllowedToConfigPools) {
+		this.poolAdminAllowedToConfigPools = poolAdminAllowedToConfigPools;
+		setStringProperty(POOL_ADMIN_POOLS, Boolean.toString(poolAdminAllowedToConfigPools), true);
+	}
+
+	public boolean isPoolAdminAllowedToConfigItemTypes() {
+		return poolAdminAllowedToConfigItemTypes;
+	}
+
+	public void setPoolAdminAllowedToConfigItemTypes(boolean poolAdminAllowedToConfigItemTypes) {
+		this.poolAdminAllowedToConfigItemTypes = poolAdminAllowedToConfigItemTypes;
+		setStringProperty(POOL_ADMIN_ITEM_TYPES, Boolean.toString(poolAdminAllowedToConfigItemTypes), true);
+	}
+
+	public boolean isPoolAdminAllowedToConfigEducationalContext() {
+		return poolAdminAllowedToConfigEducationalContext;
+	}
+
+	public void setPoolAdminAllowedToConfigEducationalContext(boolean poolAdminAllowedToConfigEducationalContext) {
+		this.poolAdminAllowedToConfigEducationalContext = poolAdminAllowedToConfigEducationalContext;
+		setStringProperty(POOL_ADMIN_EDUCATIONAL_CONTEXT, Boolean.toString(poolAdminAllowedToConfigEducationalContext), true);
+	}
+
+	public boolean isPoolAdminAllowedToConfigLicenses() {
+		return poolAdminAllowedToConfigLicenses;
+	}
+
+	public void setPoolAdminAllowedToConfigLicenses(boolean poolAdminAllowedToConfigLicenses) {
+		this.poolAdminAllowedToConfigLicenses = poolAdminAllowedToConfigLicenses;
+		setStringProperty(POOL_ADMIN_LICENSES, Boolean.toString(poolAdminAllowedToConfigLicenses), true);
+	}
+	
 }
diff --git a/src/main/java/org/olat/modules/qpool/security/ProcesslessSecurityCallback.java b/src/main/java/org/olat/modules/qpool/security/ProcesslessSecurityCallback.java
index c52e2f0b3f3..e338252e59e 100644
--- a/src/main/java/org/olat/modules/qpool/security/ProcesslessSecurityCallback.java
+++ b/src/main/java/org/olat/modules/qpool/security/ProcesslessSecurityCallback.java
@@ -21,7 +21,9 @@ package org.olat.modules.qpool.security;
 
 import org.olat.modules.qpool.QuestionItemSecurityCallback;
 import org.olat.modules.qpool.QuestionItemView;
+import org.olat.modules.qpool.QuestionPoolModule;
 import org.olat.modules.qpool.ui.QuestionItemsSource;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
@@ -37,8 +39,12 @@ public class ProcesslessSecurityCallback implements QuestionItemSecurityCallback
 
 	private QuestionItemView itemView;
 	private QuestionItemsSource questionItemSource;
-	private boolean isAdmin = false;
+	private boolean admin = false;
+	private boolean poolAdmin = false;
 
+	@Autowired
+	private QuestionPoolModule qpoolModule;
+	
 	@Override
 	public void setQuestionItemView(QuestionItemView itemView) {
 		this.itemView = itemView;
@@ -51,22 +57,31 @@ public class ProcesslessSecurityCallback implements QuestionItemSecurityCallback
 
 	@Override
 	public void setAdmin(boolean admin) {
-		this.isAdmin = admin;
+		this.admin = admin;
+	}
+
+	@Override
+	public void setPoolAdmin(boolean poolAdmin) {
+		this.poolAdmin = poolAdmin;
 	}
 	
 	@Override
 	public boolean canEditQuestion() {
-		return isAdmin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
+		return admin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
 	}
 
 	@Override
 	public boolean canEditMetadata() {
-		return isAdmin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
+		return admin 
+				|| itemView.isAuthor()
+				|| itemView.isEditableInPool()
+				|| itemView.isEditableInShare()
+				|| (poolAdmin && qpoolModule.isPoolAdminAllowedToEditMetadata());
 	}
 
 	@Override
 	public boolean canRemoveTaxonomy() {
-		return isAdmin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
+		return admin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
 	}
 
 	@Override
@@ -86,38 +101,58 @@ public class ProcesslessSecurityCallback implements QuestionItemSecurityCallback
 
 	@Override
 	public boolean canSetDraft() {
-		return isAdmin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
+		return admin
+				|| itemView.isAuthor()
+				|| itemView.isEditableInPool()
+				|| itemView.isEditableInShare()
+				|| (poolAdmin && qpoolModule.isPoolAdminAllowedToEditStatus());
 	}
 
 	@Override
 	public boolean canSetRevised() {
-		return isAdmin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
+		return admin
+				|| itemView.isAuthor()
+				|| itemView.isEditableInPool()
+				|| itemView.isEditableInShare()
+				|| (poolAdmin && qpoolModule.isPoolAdminAllowedToEditStatus());
 	}
 
 	@Override
 	public boolean canSetReview() {
-		return isAdmin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
+		return admin
+				|| itemView.isAuthor()
+				|| itemView.isEditableInPool()
+				|| itemView.isEditableInShare()
+				|| (poolAdmin && qpoolModule.isPoolAdminAllowedToEditStatus());
 	}
 
 	@Override
 	public boolean canSetFinal() {
-		return isAdmin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
+		return admin
+				|| itemView.isAuthor()
+				|| itemView.isEditableInPool()
+				|| itemView.isEditableInShare()
+				|| (poolAdmin && qpoolModule.isPoolAdminAllowedToEditStatus());
 	}
 
 	@Override
 	public boolean canSetEndOfLife() {
-		return isAdmin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
+		return admin
+				|| itemView.isAuthor()
+				|| itemView.isEditableInPool()
+				|| itemView.isEditableInShare()
+				|| (poolAdmin && qpoolModule.isPoolAdminAllowedToEditStatus());
 	}
 
 	@Override
 	public boolean canDelete() {
-		return isAdmin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
+		return admin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
 	}
 
 	@Override
 	public boolean canRemove() {
 		return  questionItemSource.isRemoveEnabled()
-				&& (isAdmin || itemView.isAuthor());
+				&& (admin || itemView.isAuthor());
 	}
 
 	@Override
@@ -127,7 +162,7 @@ public class ProcesslessSecurityCallback implements QuestionItemSecurityCallback
 
 	@Override
 	public boolean canChangeVersion() {
-		return isAdmin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
+		return admin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare();
 	}
 
 }
diff --git a/src/main/java/org/olat/modules/qpool/security/QPoolSecurityCallbackFactory.java b/src/main/java/org/olat/modules/qpool/security/QPoolSecurityCallbackFactory.java
index 3f775f9e078..9c1aaded4cc 100644
--- a/src/main/java/org/olat/modules/qpool/security/QPoolSecurityCallbackFactory.java
+++ b/src/main/java/org/olat/modules/qpool/security/QPoolSecurityCallbackFactory.java
@@ -42,8 +42,8 @@ public class QPoolSecurityCallbackFactory {
 	@Autowired
 	private QuestionPoolModule qpoolModule;
 
-	public QuestionItemSecurityCallback createQuestionItemSecurityCallback(QuestionItemView itemView, QuestionItemsSource questionItemSource,
-			boolean isOLATAdmin) {
+	public QuestionItemSecurityCallback createQuestionItemSecurityCallback(QuestionItemView itemView,
+			QuestionItemsSource questionItemSource, Roles roles) {
 		QuestionItemSecurityCallback securityCallback;
 		if (qpoolModule.isReviewProcessEnabled()) {
 			securityCallback = CoreSpringFactory.getImpl(ReviewProcessSecurityCallback.class);
@@ -52,17 +52,15 @@ public class QPoolSecurityCallbackFactory {
 		}
 		securityCallback.setQuestionItemView(itemView);
 		securityCallback.setQuestionItemSource(questionItemSource);
-		securityCallback.setAdmin(isOLATAdmin);
+		securityCallback.setAdmin(roles.isOLATAdmin());
+		securityCallback.setPoolAdmin(roles.isPoolAdmin());
 		return securityCallback;
 	}
 
 	public QPoolSecurityCallback createQPoolSecurityCallback(Roles roles) {
-		QPoolSecurityCallback securityCallback;
-		if (roles.isOLATAdmin()) {
-			securityCallback = CoreSpringFactory.getImpl(AdministratorQPoolSecurityCallback.class);
-		} else {
-			securityCallback = CoreSpringFactory.getImpl(RegularQPoolSecurityCallback.class);
-		}
+		QPoolSecurityCallback securityCallback = CoreSpringFactory.getImpl(QPoolSecurityCallbackImpl.class);
+		securityCallback.setAdmin(roles.isOLATAdmin());
+		securityCallback.setPoolAdmin(roles.isPoolAdmin());
 		return securityCallback;
 	}
 
diff --git a/src/main/java/org/olat/modules/qpool/security/AdministratorQPoolSecurityCallback.java b/src/main/java/org/olat/modules/qpool/security/QPoolSecurityCallbackImpl.java
similarity index 57%
rename from src/main/java/org/olat/modules/qpool/security/AdministratorQPoolSecurityCallback.java
rename to src/main/java/org/olat/modules/qpool/security/QPoolSecurityCallbackImpl.java
index 7c5d33c8cad..bde9a0c40a3 100644
--- a/src/main/java/org/olat/modules/qpool/security/AdministratorQPoolSecurityCallback.java
+++ b/src/main/java/org/olat/modules/qpool/security/QPoolSecurityCallbackImpl.java
@@ -33,11 +33,24 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @Scope("prototype")
-public class AdministratorQPoolSecurityCallback implements QPoolSecurityCallback {
+public class QPoolSecurityCallbackImpl implements QPoolSecurityCallback {
 
+	private boolean admin = false;
+	private boolean poolAdmin = false;
+	
 	@Autowired
 	private QuestionPoolModule qpoolModule;
-	
+
+	@Override
+	public void setAdmin(boolean admin) {
+		this.admin = admin;
+	}
+
+	@Override
+	public void setPoolAdmin(boolean poolAdmin) {
+		this.poolAdmin = poolAdmin;
+	}
+
 	@Override
 	public boolean canUseCollections() {
 		return qpoolModule.isCollectionsEnabled();
@@ -59,9 +72,38 @@ public class AdministratorQPoolSecurityCallback implements QPoolSecurityCallback
 	}
 
 	@Override
-	public boolean canAdmin() {
-		return true;
+	public boolean canEditAllQuestions() {
+		return admin || (poolAdmin && qpoolModule.isPoolAdminAllowedToEditMetadata());
+	}
+
+	@Override
+	public boolean canConfigReviewProcess() {
+		return admin || (poolAdmin && qpoolModule.isPoolAdminAllowedToConfigReviewProcess());
+	}
+
+	@Override
+	public boolean canConfigTaxonomies() {
+		return admin || (poolAdmin && qpoolModule.isPoolAdminAllowedToConfigTaxonomy());
 	}
 
+	@Override
+	public boolean canConfigPools() {
+		return admin || (poolAdmin && qpoolModule.isPoolAdminAllowedToConfigPools());
+	}
+
+	@Override
+	public boolean canConfigItemTypes() {
+		return admin || (poolAdmin && qpoolModule.isPoolAdminAllowedToConfigItemTypes());
+	}
+
+	@Override
+	public boolean canConfigEducationalContext() {
+		return admin || (poolAdmin && qpoolModule.isPoolAdminAllowedToConfigEducationalContext());
+	}
+
+	@Override
+	public boolean canConfigLicences() {
+		return admin || (poolAdmin && qpoolModule.isPoolAdminAllowedToConfigLicenses());
+	}
 
 }
diff --git a/src/main/java/org/olat/modules/qpool/security/RegularQPoolSecurityCallback.java b/src/main/java/org/olat/modules/qpool/security/RegularQPoolSecurityCallback.java
deleted file mode 100644
index dd5bac65a24..00000000000
--- a/src/main/java/org/olat/modules/qpool/security/RegularQPoolSecurityCallback.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * <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.qpool.security;
-
-import org.olat.modules.qpool.QPoolSecurityCallback;
-import org.olat.modules.qpool.QuestionPoolModule;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Scope;
-import org.springframework.stereotype.Component;
-
-/**
- * 
- * Initial date: 05.12.2017<br>
- * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
- *
- */
-@Component
-@Scope("prototype")
-public class RegularQPoolSecurityCallback implements QPoolSecurityCallback {
-
-	@Autowired
-	private QuestionPoolModule qpoolModule;
-
-	@Override
-	public boolean canUseCollections() {
-		return qpoolModule.isCollectionsEnabled();
-	}
-
-	@Override
-	public boolean canUsePools() {
-		return qpoolModule.isPoolsEnabled();
-	}
-
-	@Override
-	public boolean canUseGroups() {
-		return qpoolModule.isSharesEnabled();
-	}
-
-	@Override
-	public boolean canUseReviewProcess() {
-		return qpoolModule.isReviewProcessEnabled();
-	}
-
-	@Override
-	public boolean canAdmin() {
-		return false;
-	}
-
-}
diff --git a/src/main/java/org/olat/modules/qpool/security/ReviewProcessSecurityCallback.java b/src/main/java/org/olat/modules/qpool/security/ReviewProcessSecurityCallback.java
index 6d95b93abb5..b5bcc7f8675 100644
--- a/src/main/java/org/olat/modules/qpool/security/ReviewProcessSecurityCallback.java
+++ b/src/main/java/org/olat/modules/qpool/security/ReviewProcessSecurityCallback.java
@@ -24,6 +24,7 @@ import java.util.Collection;
 
 import org.olat.modules.qpool.QuestionItemSecurityCallback;
 import org.olat.modules.qpool.QuestionItemView;
+import org.olat.modules.qpool.QuestionPoolModule;
 import org.olat.modules.qpool.QuestionStatus;
 import org.olat.modules.qpool.ReviewService;
 import org.olat.modules.qpool.ui.QuestionItemsSource;
@@ -49,8 +50,11 @@ public class ReviewProcessSecurityCallback implements QuestionItemSecurityCallba
 
 	private QuestionItemView itemView;
 	private QuestionItemsSource questionItemSource;
-	private boolean isAdmin = false;
+	private boolean admin = false;
+	private boolean poolAdmin = false;
 	
+	@Autowired
+	private QuestionPoolModule qpoolModule;
 	@Autowired
 	private ReviewService reviewService;
 
@@ -66,38 +70,43 @@ public class ReviewProcessSecurityCallback implements QuestionItemSecurityCallba
 
 	@Override
 	public void setAdmin(boolean admin) {
-		this.isAdmin = admin;
+		this.admin = admin;
+	}
+
+	@Override
+	public void setPoolAdmin(boolean poolAdmin) {
+		this.poolAdmin = poolAdmin;
 	}
 
 	@Override
 	public boolean canEditQuestion() {
 		return reviewService.isEditableQuestionStatus(itemView.getQuestionStatus())
-				&& (isAdmin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare()) ;
+				&& (admin || itemView.isAuthor() || itemView.isEditableInPool() || itemView.isEditableInShare()) ;
 	}
 
 	@Override
 	public boolean canEditMetadata() {
-		return isAdmin || itemView.isAuthor() || itemView.isManager();
+		return admin || itemView.isAuthor() || itemView.isManager() || (poolAdmin && qpoolModule.isPoolAdminAllowedToEditMetadata());
 	}
 
 	@Override
 	public boolean canRemoveTaxonomy() {
 		return QuestionStatus.draft.equals(itemView.getQuestionStatus())
-				&& (isAdmin || itemView.isAuthor() || itemView.isManager());
+				&& (admin || itemView.isAuthor() || itemView.isManager());
 	}
 
 	@Override
 	public boolean canStartReview() {
 		return itemView.isReviewableFormat()
 				&& reviewService.isEditableQuestionStatus(itemView.getQuestionStatus())
-				&& (isAdmin || itemView.isAuthor());
+				&& (admin || itemView.isAuthor());
 	}
 
 	@Override
 	public boolean canReviewNotStartable() {
 		return !itemView.isReviewableFormat()
 				&& reviewService.isEditableQuestionStatus(itemView.getQuestionStatus())
-				&& (isAdmin || itemView.isAuthor());
+				&& (admin || itemView.isAuthor());
 	}
 	
 	@Override
@@ -109,13 +118,13 @@ public class ReviewProcessSecurityCallback implements QuestionItemSecurityCallba
 
 	@Override
 	public boolean canSetDraft() {
-		return isAdmin;
+		return admin;
 	}
 
 	@Override
 	public boolean canSetRevised() {
 		return itemView.isReviewableFormat()
-				&& (isAdmin || itemView.isManager());
+				&& (admin || itemView.isManager() || (poolAdmin && qpoolModule.isPoolAdminAllowedToEditStatus()));
 	}
 
 	@Override
@@ -126,24 +135,24 @@ public class ReviewProcessSecurityCallback implements QuestionItemSecurityCallba
 	@Override
 	public boolean canSetFinal() {
 		return itemView.isReviewableFormat()
-				&& (isAdmin || itemView.isManager());
+				&& (admin || itemView.isManager() || (poolAdmin && qpoolModule.isPoolAdminAllowedToEditStatus()));
 	}
 
 	@Override
 	public boolean canSetEndOfLife() {
-		return isAdmin || itemView.isManager();
+		return admin || itemView.isManager() || (poolAdmin && qpoolModule.isPoolAdminAllowedToEditStatus());
 	}
 
 	@Override
 	public boolean canDelete() {
 		return DELETABLE_STATES.contains(itemView.getQuestionStatus())
-				&& (isAdmin || itemView.isManager());
+				&& (admin || itemView.isManager());
 	}
 
 	@Override
 	public boolean canRemove() {
 		return questionItemSource.isRemoveEnabled()
-				&& (isAdmin || itemView.isAuthor());
+				&& (admin || itemView.isAuthor());
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/modules/qpool/ui/AbstractItemListController.java b/src/main/java/org/olat/modules/qpool/ui/AbstractItemListController.java
index c479afa648a..3c01421d8f4 100644
--- a/src/main/java/org/olat/modules/qpool/ui/AbstractItemListController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/AbstractItemListController.java
@@ -57,6 +57,7 @@ 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.id.OLATResourceable;
+import org.olat.core.id.Roles;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.event.EventBus;
 import org.olat.core.util.event.GenericEventListener;
@@ -105,7 +106,7 @@ public abstract class AbstractItemListController extends FormBasicController
 	
 	private EventBus eventBus;
 	private QuestionItemsSource itemsSource;
-	private final boolean isOLATAdmin;
+	private final Roles roles;
 	
 	public AbstractItemListController(UserRequest ureq, WindowControl wControl, QPoolSecurityCallback securityCallback,
 			QuestionItemsSource source, String key) {
@@ -129,7 +130,7 @@ public abstract class AbstractItemListController extends FormBasicController
 		this.securityCallback = securityCallback;
 		this.prefsKey = key;
 		this.itemsSource = source;
-		this.isOLATAdmin = ureq.getUserSession().getRoles().isOLATAdmin();
+		this.roles = ureq.getUserSession().getRoles();
 		this.restrictToFormat = restrictToFormat;
 
 		eventBus = ureq.getUserSession().getSingleUserEventCenter();
@@ -456,7 +457,7 @@ public abstract class AbstractItemListController extends FormBasicController
 	protected ItemRow forgeRow(QuestionItemView item) {
 		boolean marked = item.isMarked();
 		QuestionItemSecurityCallback securityCallback = qpoolSecurityCallbackFactory
-				.createQuestionItemSecurityCallback(item, getSource(), isOLATAdmin);
+				.createQuestionItemSecurityCallback(item, getSource(), roles);
 		ItemRow row = new ItemRow(item, securityCallback);
 		FormLink markLink = uifactory.addFormLink("mark_" + row.getKey(), "mark", "&nbsp;", null, null, Link.NONTRANSLATED);
 		markLink.setIconLeftCSS(marked ? Mark.MARK_CSS_LARGE : Mark.MARK_ADD_CSS_LARGE);
@@ -469,7 +470,7 @@ public abstract class AbstractItemListController extends FormBasicController
 	protected ItemRow wrapNewItem(QuestionItem item) {
 		ItemWrapper itemWrapper = ItemWrapper.builder(item).setAuthor(true).create();
 		QuestionItemSecurityCallback securityCallback = qpoolSecurityCallbackFactory
-				.createQuestionItemSecurityCallback(itemWrapper, getSource(), isOLATAdmin);
+				.createQuestionItemSecurityCallback(itemWrapper, getSource(), roles);
 		return new ItemRow(itemWrapper, securityCallback);
 	}
 }
diff --git a/src/main/java/org/olat/modules/qpool/ui/_content/item_list_overview.html b/src/main/java/org/olat/modules/qpool/ui/_content/item_list_overview.html
index 033fc058937..29f9eee1ab3 100644
--- a/src/main/java/org/olat/modules/qpool/ui/_content/item_list_overview.html
+++ b/src/main/java/org/olat/modules/qpool/ui/_content/item_list_overview.html
@@ -1,5 +1,5 @@
 <h4>
-	<i class="o_icon o_icon-fw  o_icon_qpool"> </i>
+	<i class="o_icon o_icon-fw o_icon_qpool"> </i>
 	$r.translate("menu.pools.main")
 </h4>
 <div class="clearfix">
diff --git a/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_de.properties
index a48d80bb09d..62d0db743aa 100644
--- a/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_de.properties
@@ -108,6 +108,7 @@ menu.admin.levels.alt=Stufe
 menu.admin.licenses=Lizenz
 menu.admin.licenses.alt=Lizenz
 menu.admin.pools=Pool-Verwaltung
+menu.admin.review.process=Beurteilungsprozess
 menu.admin.studyfields=Fachbereich
 menu.admin.studyfields.alt=Fachbereich
 menu.admin.types=Fragetyp
diff --git a/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_en.properties
index c700b6eebd5..a3eff9954f2 100644
--- a/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_en.properties
@@ -108,6 +108,7 @@ menu.admin.levels.alt=Level
 menu.admin.licenses=License
 menu.admin.licenses.alt=License
 menu.admin.pools=Pool administration
+menu.admin.review.process=Review Process
 menu.admin.studyfields=Subject
 menu.admin.studyfields.alt=Subject
 menu.admin.types=Type
diff --git a/src/main/java/org/olat/modules/qpool/ui/admin/QuestionPoolAdminConfigurationController.java b/src/main/java/org/olat/modules/qpool/ui/admin/QuestionPoolAdminConfigurationController.java
index f510b2d15c9..714abd69476 100644
--- a/src/main/java/org/olat/modules/qpool/ui/admin/QuestionPoolAdminConfigurationController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/admin/QuestionPoolAdminConfigurationController.java
@@ -19,7 +19,9 @@
  */
 package org.olat.modules.qpool.ui.admin;
 
+import java.util.Collection;
 import java.util.List;
+import java.util.stream.Stream;
 
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItem;
@@ -48,12 +50,31 @@ import org.springframework.beans.factory.annotation.Autowired;
  */
 public class QuestionPoolAdminConfigurationController extends FormBasicController {
 	
-	private static final String[] onKeys = new String[] { "on" };
+	private static final String[] onKeys = { "on" };
+	private static final String POOL_MANAGER_EDIT_METADATA = "pool.manager.edit.matadata";
+	private static final String POOL_MANAGER_EDIT_STATUS = "pool.manager.edit.status";
+	private static final String POOL_MANAGER_REVIEW_PROCESS = "pool.manager.review.process";
+	private static final String POOL_MANAGER_TAXONOMY = "pool.manager.taxonomy";
+	private static final String POOL_MANAGER_POOLS = "pool.manager.pools";
+	private static final String POOL_MANAGER_ITEM_TYPES = "pool.manager.item.types";
+	private static final String POOL_MANAGER_EDUCATIONAL_CONTEXT = "pool.manager.educational.context";
+	private static final String POOL_MANAGER_LICENSES = "pool.manager.licenses";
+	private static final String[] POOL_MANAGER_RIGHTS_KEYS = {
+			POOL_MANAGER_EDIT_METADATA,
+			POOL_MANAGER_EDIT_STATUS,
+			POOL_MANAGER_REVIEW_PROCESS,
+			POOL_MANAGER_TAXONOMY,
+			POOL_MANAGER_POOLS,
+			POOL_MANAGER_ITEM_TYPES,
+			POOL_MANAGER_EDUCATIONAL_CONTEXT,
+			POOL_MANAGER_LICENSES
+	};
 	
 	private MultipleSelectionElement reviewProcessEnabledEl;
 	private MultipleSelectionElement collectionsEnabledEl;
 	private MultipleSelectionElement poolsEnabledEl;
 	private MultipleSelectionElement sharesEnabledEl;
+	private MultipleSelectionElement poolManagerRightsEl;
 	private SingleSelection taxonomyTreeEl;
 	
 	private CloseableModalController closeableModalCtrl;
@@ -69,36 +90,36 @@ public class QuestionPoolAdminConfigurationController extends FormBasicControlle
 	private QPoolService qpoolService;
 	
 	public QuestionPoolAdminConfigurationController(UserRequest ureq, WindowControl wControl) {
-		super(ureq, wControl);
+		super(ureq, wControl, "admin_config");
 		
 		initForm(ureq);
 	}
 
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
-		setFormTitle("admin.configuration.title");
+		FormLayoutContainer moduleCont = FormLayoutContainer.createDefaultFormLayout("module", getTranslator());
+		moduleCont.setFormTitle(translate("admin.configuration.title"));
+		moduleCont.setRootForm(mainForm);
+		formLayout.add("module", moduleCont);
 		
 		String[] onValues = new String[] { translate("on") };
-		reviewProcessEnabledEl = uifactory.addCheckboxesHorizontal("review.process.enabled", formLayout, onKeys, onValues);
+		reviewProcessEnabledEl = uifactory.addCheckboxesHorizontal("review.process.enabled", moduleCont, onKeys, onValues);
 		reviewProcessEnabledEl.addActionListener(FormEvent.ONCHANGE);
 		if (qpoolModule.isReviewProcessEnabled()) {
 			reviewProcessEnabledEl.select(onKeys[0], true);
 		}
 		
-		collectionsEnabledEl = uifactory.addCheckboxesHorizontal("collections.enabled", formLayout, onKeys, onValues);
-		collectionsEnabledEl.addActionListener(FormEvent.ONCHANGE);
+		collectionsEnabledEl = uifactory.addCheckboxesHorizontal("collections.enabled", moduleCont, onKeys, onValues);
 		if (qpoolModule.isCollectionsEnabled()) {
 			collectionsEnabledEl.select(onKeys[0], true);
 		}
 		
-		poolsEnabledEl = uifactory.addCheckboxesHorizontal("pools.enabled", formLayout, onKeys, onValues);
-		poolsEnabledEl.addActionListener(FormEvent.ONCHANGE);
+		poolsEnabledEl = uifactory.addCheckboxesHorizontal("pools.enabled", moduleCont, onKeys, onValues);
 		if (qpoolModule.isPoolsEnabled()) {
 			poolsEnabledEl.select(onKeys[0], true);
 		}
 
-		sharesEnabledEl = uifactory.addCheckboxesHorizontal("shares.enabled", formLayout, onKeys, onValues);
-		sharesEnabledEl.addActionListener(FormEvent.ONCHANGE);
+		sharesEnabledEl = uifactory.addCheckboxesHorizontal("shares.enabled", moduleCont, onKeys, onValues);
 		if (qpoolModule.isSharesEnabled()) {
 			sharesEnabledEl.select(onKeys[0], true);
 		}
@@ -115,7 +136,7 @@ public class QuestionPoolAdminConfigurationController extends FormBasicControlle
 		}
 		
 		String selectedTaxonomyQPoolKey = qpoolModule.getTaxonomyQPoolKey();
-		taxonomyTreeEl = uifactory.addDropdownSingleselect("selected.taxonomy.tree", formLayout, taxonomyKeys, taxonomyValues, null);
+		taxonomyTreeEl = uifactory.addDropdownSingleselect("selected.taxonomy.tree", moduleCont, taxonomyKeys, taxonomyValues, null);
 		taxonomyTreeEl.setEnabled(false);
 		if(StringHelper.containsNonWhitespace(selectedTaxonomyQPoolKey)) {
 			for(String taxonomyKey:taxonomyKeys) {
@@ -125,11 +146,36 @@ public class QuestionPoolAdminConfigurationController extends FormBasicControlle
 			}
 		}
 		
+		FormLayoutContainer poolManagerRightsCont = FormLayoutContainer.createDefaultFormLayout("poolManagerRights", getTranslator());
+		poolManagerRightsCont.setFormTitle(translate("admin.pool.manager.title"));
+		poolManagerRightsCont.setRootForm(mainForm);
+		formLayout.add("poolManagerRights", poolManagerRightsCont);
+		
+		poolManagerRightsEl = uifactory.addCheckboxesVertical("pool.manager.allowed", poolManagerRightsCont,
+				POOL_MANAGER_RIGHTS_KEYS, translateKeys(POOL_MANAGER_RIGHTS_KEYS), 1);
+		poolManagerRightsEl.select(POOL_MANAGER_EDIT_METADATA, qpoolModule.isPoolAdminAllowedToEditMetadata());
+		poolManagerRightsEl.select(POOL_MANAGER_EDIT_STATUS, qpoolModule.isPoolAdminAllowedToEditStatus());
+		poolManagerRightsEl.select(POOL_MANAGER_REVIEW_PROCESS, qpoolModule.isPoolAdminAllowedToConfigReviewProcess());
+		poolManagerRightsEl.select(POOL_MANAGER_TAXONOMY, qpoolModule.isPoolAdminAllowedToConfigTaxonomy());
+		poolManagerRightsEl.select(POOL_MANAGER_POOLS, qpoolModule.isPoolAdminAllowedToConfigPools());
+		poolManagerRightsEl.select(POOL_MANAGER_ITEM_TYPES, qpoolModule.isPoolAdminAllowedToConfigItemTypes());
+		poolManagerRightsEl.select(POOL_MANAGER_EDUCATIONAL_CONTEXT, qpoolModule.isPoolAdminAllowedToConfigEducationalContext());
+		poolManagerRightsEl.select(POOL_MANAGER_LICENSES, qpoolModule.isPoolAdminAllowedToConfigLicenses());
+		
+		FormLayoutContainer buttonsWrapperCont = FormLayoutContainer.createDefaultFormLayout("global", getTranslator());
+		buttonsWrapperCont.setRootForm(mainForm);
+		formLayout.add("buttonsWrapper", buttonsWrapperCont);
 		FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
-		formLayout.add(buttonsCont);
+		buttonsWrapperCont.add(buttonsCont);
 		uifactory.addFormSubmitButton("save", buttonsCont);
 	}
 
+	private String[] translateKeys(String[] keys) {
+		return Stream.of(keys)
+				.map(key -> getTranslator().translate(key))
+				.toArray(String[]::new);
+	}
+
 	@Override
 	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
 		if (reviewProcessEnabledEl == source) {
@@ -192,6 +238,24 @@ public class QuestionPoolAdminConfigurationController extends FormBasicControlle
 		
 		String selectedTaxonomyQPoolKey = taxonomyTreeEl.getSelectedKey();
 		qpoolModule.setTaxonomyQPoolKey(selectedTaxonomyQPoolKey);
+		
+		Collection<String> selectedPoolManagerRights = poolManagerRightsEl.getSelectedKeys();
+		boolean poolAdminAllowedToEditMetadata = selectedPoolManagerRights.contains(POOL_MANAGER_EDIT_METADATA);
+		qpoolModule.setPoolAdminAllowedToEditMetadata(poolAdminAllowedToEditMetadata);
+		boolean poolAdminAllowedToEditStatus = selectedPoolManagerRights.contains(POOL_MANAGER_EDIT_STATUS);
+		qpoolModule.setPoolAdminAllowedToEditStatus(poolAdminAllowedToEditStatus);
+		boolean poolAdminAllowedToConfigReviewProcess = selectedPoolManagerRights.contains(POOL_MANAGER_REVIEW_PROCESS);
+		qpoolModule.setPoolAdminAllowedToConfigReviewProcess(poolAdminAllowedToConfigReviewProcess );
+		boolean poolAdminAllowedToConfigTaxonomy = selectedPoolManagerRights.contains(POOL_MANAGER_TAXONOMY);
+		qpoolModule.setPoolAdminAllowedToConfigTaxonomy(poolAdminAllowedToConfigTaxonomy);
+		boolean poolAdminAllowedToConfigPools = selectedPoolManagerRights.contains(POOL_MANAGER_POOLS);
+		qpoolModule.setPoolAdminAllowedToConfigPools(poolAdminAllowedToConfigPools);
+		boolean poolAdminAllowedToConfigItemTypes = selectedPoolManagerRights.contains(POOL_MANAGER_ITEM_TYPES);
+		qpoolModule.setPoolAdminAllowedToConfigItemTypes(poolAdminAllowedToConfigItemTypes);
+		boolean poolAdminAllowedToConfigEducationalContext = selectedPoolManagerRights.contains(POOL_MANAGER_EDUCATIONAL_CONTEXT);
+		qpoolModule.setPoolAdminAllowedToConfigEducationalContext(poolAdminAllowedToConfigEducationalContext);
+		boolean poolAdminAllowedToConfigLicenses = selectedPoolManagerRights.contains(POOL_MANAGER_LICENSES);
+		qpoolModule.setPoolAdminAllowedToConfigLicenses(poolAdminAllowedToConfigLicenses);
 	}
 	
 	@Override
diff --git a/src/main/java/org/olat/modules/qpool/ui/admin/_content/admin_config.html b/src/main/java/org/olat/modules/qpool/ui/admin/_content/admin_config.html
new file mode 100644
index 00000000000..52ad99a8004
--- /dev/null
+++ b/src/main/java/org/olat/modules/qpool/ui/admin/_content/admin_config.html
@@ -0,0 +1,3 @@
+$r.render("module")
+$r.render("poolManagerRights")
+$r.render("buttonsWrapper")
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/qpool/ui/admin/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/qpool/ui/admin/_i18n/LocalStrings_de.properties
index cd9572d6d12..88dfd21e4d3 100644
--- a/src/main/java/org/olat/modules/qpool/ui/admin/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/qpool/ui/admin/_i18n/LocalStrings_de.properties
@@ -3,6 +3,7 @@ add.taxonomyLevel=Fachbereich erstellen
 admin.configuration.title=Fragenpool
 admin.levels.intro=Erstellen Sie die verf\u00FCgbaren Stufen die f\u00FCr Ihr Ausbildungslevel Sinn machen. Beispiele f\u00FCr Stufen im schulischen Kontext sind\: Unterstufe, Oberstufe, Gymnasium, Bachelor, Master. In einem Unternehmenskontext k\u00F6nnten Stufen so aussehen\: ohne Berufsausbildung, mit Berufsausbildung, F\u00FChrungsfunktion, Administration, Kader, Management
 admin.licenses.intro=Erstellen Sie zus\u00E4tzliche Lizenztypen wenn die Standard Lizenztypen von OpenOLAT nicht gen\u00FCgen. Die Standard Lizenztypen k\u00F6nnen nicht gel\u00F6scht werden.
+admin.pool.manager.title=Rechte Poolverwalter
 admin.pools.intro=Erstellen Sie einen oder mehrere Fragenpools. Ein Fragenpool ist eine Fragendatenbank die allen Autoren des Systems f\u00FCr den Austausch von Fragen zur Verf\u00FCgung steht (\u00D6ffentlich). Optional l\u00E4sst sich ein Fragenpool auch auf einige wenige Autoren einschr\u00E4nken, z.B. die Mitarbeiter einer Abteilung (Nicht \u00F6ffentlich).
 admin.review.process.decision.type=Beurteilungsmethode
 admin.review.process.title=Beurteilungsprozess
@@ -43,6 +44,15 @@ license.key=Lizenz
 lower.limit=Untergrenze f\u00FCr positive Beurteilung
 lower.limit.provider.name=Untergrenze
 number.of.ratings=Anzahl Beurteilungen pro Frage
+pool.manager.allowed=Der Poolverwalter darf
+pool.manager.edit.matadata=alle Fragen sehen und die Metadaten bearbeiten
+pool.manager.edit.status=den Status einer Frage beliebig \u00E4ndern
+pool.manager.educational.context=die Einstellungen "$:\segment.educational.context" bearbeiten
+pool.manager.licenses=die Einstellungen "$:\segment.licenses" bearbeiten
+pool.manager.item.types=die Einstellungen "$:\segment.item.types" bearbeiten
+pool.manager.pools=die Einstellungen "$:\segment.pools" bearbeiten
+pool.manager.review.process=die Einstellungen "$:\segment.review.process" bearbeiten
+pool.manager.taxonomy=die Einstellungen "$:\segment.taxonomy" bearbeiten
 pools.enabled=Pools
 reset.status=Alle Fragen in den Status "draft" zur\u00FCcksetzen.
 review.process.confirm.enable.button=Einschalten
diff --git a/src/main/java/org/olat/modules/qpool/ui/admin/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/qpool/ui/admin/_i18n/LocalStrings_en.properties
index b1813d49432..dc4a4e4e2bf 100644
--- a/src/main/java/org/olat/modules/qpool/ui/admin/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/qpool/ui/admin/_i18n/LocalStrings_en.properties
@@ -3,6 +3,7 @@ add.taxonomyLevel=Create subject
 admin.configuration.title=Question bank
 admin.levels.intro=Create those levels that apply to your organisations educational level. Examples for levels in an academic context could be elementary school, middle school, high school, bachelor, master. Within a corporation context, levels could be the following\: without apprenticeship, with vocational training, administration, middle management, CO
 admin.licenses.intro=You can create more license types if the default license types are not sufficient. Please note that the default licenses cannot be deleted.
+admin.pool.manager.title=Rights pool manager
 admin.pools.intro=Create one or more question pools. A pool is an question bank that allows all users with author rights the exchange of question items (public access). You can also opt for a pool with only a select few authors, e.g. a departments' staff (private access).
 admin.review.process.decision.type=Decision method
 admin.review.process.title=Review process
@@ -42,6 +43,15 @@ license.id=ID
 license.key=License
 lower.limit=Lower limit for positive decision
 number.of.ratings=Number of ratings per question
+pool.manager.allowed=The pool manager is allowed to
+pool.manager.edit.matadata=see all questions and edit the metadata
+pool.manager.edit.status=change the status of a question
+pool.manager.educational.context=edit the settings "$:\segment.educational.context"
+pool.manager.licenses=edit the settings "$:\segment.licenses"
+pool.manager.item.types=edit the settings "$:\segment.item.types"
+pool.manager.pools=edit the settings "$:\segment.pools"
+pool.manager.review.process=edit the settings "$:\segment.review.process"
+pool.manager.taxonomy=edit the settings "$:\segment.taxonomy"
 pools.enabled=Pools
 reset.status=Reset all questions to state "draft".
 review.process.confirm.enable.button=Enable
diff --git a/src/main/java/org/olat/modules/qpool/ui/metadata/ExtendedSearchController.java b/src/main/java/org/olat/modules/qpool/ui/metadata/ExtendedSearchController.java
index 4a1c9da9455..7db4e1da0ef 100644
--- a/src/main/java/org/olat/modules/qpool/ui/metadata/ExtendedSearchController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/metadata/ExtendedSearchController.java
@@ -68,6 +68,7 @@ public class ExtendedSearchController extends FormBasicController implements Ext
 	private ExtendedSearchPrefs prefs;
 	private boolean enabled = true;
 	
+	
 	@Autowired
 	private QPoolService qpoolService;
 	@Autowired
diff --git a/src/main/java/org/olat/modules/qpool/ui/tree/QuestionPoolMenuTreeModel.java b/src/main/java/org/olat/modules/qpool/ui/tree/QuestionPoolMenuTreeModel.java
index beab2655ca0..602a8ded935 100644
--- a/src/main/java/org/olat/modules/qpool/ui/tree/QuestionPoolMenuTreeModel.java
+++ b/src/main/java/org/olat/modules/qpool/ui/tree/QuestionPoolMenuTreeModel.java
@@ -231,11 +231,14 @@ public class QuestionPoolMenuTreeModel extends GenericTreeModel implements DnDTr
 		}
 		
 		//administration
-		if(securityCallback.canAdmin()) {
-			TreeNode adminNode = new AdministrationTreeNode(translator.translate("menu.admin"));
-			rootNode.addChild(adminNode);
-			buildAdminSubTreeModel(adminNode);
+		TreeNode adminNode = new AdministrationTreeNode(translator.translate("menu.admin"));
+		rootNode.addChild(adminNode);
+		buildAdminSubTreeModel(adminNode);
+		if (adminNode.getChildCount() > 0) {
 			setFirstChildAsDelegate(adminNode);
+		} else {
+			// Admin tree node should not be visible if user has no particular admin rights.
+			rootNode.remove(adminNode);
 		}
 	}
 	
@@ -343,23 +346,40 @@ public class QuestionPoolMenuTreeModel extends GenericTreeModel implements DnDTr
 	private void buildAdminSubTreeModel(TreeNode adminNode) {
 		adminNode.removeAllChildren();
 		
-		TreeNode node = new AllQuestionsTreeNode(stackPanel, securityCallback, translator.translate("menu.all.questions"));
-		adminNode.addChild(node);
-		
-		node = new TaxonomyAdminTreeNode(translator.translate("menu.admin.studyfields"));
-		adminNode.addChild(node);
+		if (securityCallback.canEditAllQuestions()) {
+			TreeNode node = new AllQuestionsTreeNode(stackPanel, securityCallback, translator.translate("menu.all.questions"));
+			adminNode.addChild(node);
+		}
 		
-		node = new PoolsAdminTreeNode(translator.translate("menu.admin.pools"));
-		adminNode.addChild(node);
+		if (securityCallback.canConfigReviewProcess()) {
+			TreeNode node = new ReviewProcessAdminTreeNode(translator.translate("menu.admin.review.process"));
+			adminNode.addChild(node);
+		}
 		
-		node = new QItemTypesAdminTreeNode(translator.translate("menu.admin.types"));
-		adminNode.addChild(node);
+		if (securityCallback.canConfigTaxonomies()) {
+			TreeNode node = new TaxonomyAdminTreeNode(translator.translate("menu.admin.studyfields"));
+			adminNode.addChild(node);
+		}
 		
-		node = new QEducationalContextsAdminTreeNode(translator.translate("menu.admin.levels"));
-		adminNode.addChild(node);
+		if (securityCallback.canConfigPools()) {
+			TreeNode node = new PoolsAdminTreeNode(translator.translate("menu.admin.pools"));
+			adminNode.addChild(node);
+		}
+			
+		if (securityCallback.canConfigItemTypes()) {
+			TreeNode node = new QItemTypesAdminTreeNode(translator.translate("menu.admin.types"));
+			adminNode.addChild(node);
+		}
+			
+		if (securityCallback.canConfigEducationalContext()) {
+			TreeNode node = new QEducationalContextsAdminTreeNode(translator.translate("menu.admin.levels"));
+			adminNode.addChild(node);
+		}
 
-		node = new QLicensesAdminTreeNode(translator.translate("menu.admin.licenses"));
-		adminNode.addChild(node);
+		if (securityCallback.canConfigLicences()) {
+			TreeNode node = new QLicensesAdminTreeNode(translator.translate("menu.admin.licenses"));
+			adminNode.addChild(node);
+		}
 	}
 	
 	private void setFirstChildAsDelegate(INode node) {
diff --git a/src/main/java/org/olat/modules/qpool/ui/tree/ReviewProcessAdminTreeNode.java b/src/main/java/org/olat/modules/qpool/ui/tree/ReviewProcessAdminTreeNode.java
new file mode 100644
index 00000000000..65cecc2f51b
--- /dev/null
+++ b/src/main/java/org/olat/modules/qpool/ui/tree/ReviewProcessAdminTreeNode.java
@@ -0,0 +1,59 @@
+/**
+ * <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.qpool.ui.tree;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.tree.GenericTreeNode;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.id.OLATResourceable;
+import org.olat.core.id.context.BusinessControlFactory;
+import org.olat.core.util.resource.OresHelper;
+import org.olat.modules.qpool.ui.admin.ReviewProcessAdminController;
+
+/**
+ * 
+ * Initial date: 09.01.2018<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class ReviewProcessAdminTreeNode extends GenericTreeNode implements ControllerTreeNode {
+
+	private static final long serialVersionUID = -2738148565868342086L;
+
+	public static final OLATResourceable ORES = OresHelper.createOLATResourceableType("ReviewProcess");
+	
+	private ReviewProcessAdminController controller;
+	
+	public ReviewProcessAdminTreeNode(String title) {
+		super();
+		this.setTitle(title);
+	}
+
+	@Override
+	public Controller getController(UserRequest ureq, WindowControl wControl) {
+		if(controller == null) {
+			WindowControl swControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ureq, ORES, null,
+					wControl, true);
+			controller = new ReviewProcessAdminController(ureq, swControl);
+		} 
+		return controller;
+	}
+}
-- 
GitLab