From 7487e9290bc57359fac55ce1abbec14a81937c93 Mon Sep 17 00:00:00 2001
From: uhensler <urs.hensler@frentix.com>
Date: Wed, 27 May 2020 10:51:20 +0200
Subject: [PATCH] OO-4708: Black lists in quality generators

---
 .../generator/QualityGeneratorProvider.java   |  10 +-
 .../generator/QualityGeneratorService.java    |   9 +-
 .../manager/QualityGeneratorServiceImpl.java  |  20 +-
 .../provider/course/CourseProvider.java       |  23 +-
 .../course/manager/CourseProviderDAO.java     |  17 +-
 .../course/manager/SearchParameters.java      |  47 ++-
 .../CourseLecturesFollowUpProvider.java       |  16 +-
 .../CourseLecturesProvider.java               |  19 +-
 .../manager/CourseLecturesProviderDAO.java    |  40 ++-
 .../manager/SearchParameters.java             |  28 +-
 .../CurriculumElementProvider.java            |  23 +-
 .../manager/CurriculumElementProviderDAO.java |  18 +-
 .../manager/SearchParameters.java             |  29 +-
 .../provider/fallback/FallbackProvider.java   |  16 +-
 .../CurriculumElementBlackListController.java |  65 ++++
 .../ui/CurriculumElementListController.java   | 289 +++++++++++++++++
 ...va => CurriculumElementListDataModel.java} |   6 +-
 .../CurriculumElementWhiteListController.java | 247 +--------------
 .../generator/ui/GeneratorController.java     |  19 +-
 .../ui/GeneratorWhiteListController.java      |  32 --
 .../RepositoryEntryBlackListController.java   |  63 ++++
 .../ui/RepositoryEntryListController.java     | 291 ++++++++++++++++++
 ...java => RepositoryEntryListDataModel.java} |   6 +-
 .../RepositoryEntryWhiteListController.java   | 245 +--------------
 .../ui/_i18n/LocalStrings_de.properties       |   1 +
 .../ui/_i18n/LocalStrings_en.properties       |   1 +
 .../course/manager/CourseProviderDAOTest.java |  20 +-
 .../CourseLecturesProviderDAOTest.java        |  39 ++-
 .../CurriculumElementProviderTest.java        |   4 +-
 .../CurriculumElementProviderDAOTest.java     |  32 +-
 30 files changed, 1090 insertions(+), 585 deletions(-)
 create mode 100644 src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementBlackListController.java
 create mode 100644 src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementListController.java
 rename src/main/java/org/olat/modules/quality/generator/ui/{CurriculumElementWhiteListDataModel.java => CurriculumElementListDataModel.java} (92%)
 delete mode 100644 src/main/java/org/olat/modules/quality/generator/ui/GeneratorWhiteListController.java
 create mode 100644 src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryBlackListController.java
 create mode 100644 src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryListController.java
 rename src/main/java/org/olat/modules/quality/generator/ui/{RepositoryEntryWhiteListDataModel.java => RepositoryEntryListDataModel.java} (92%)

diff --git a/src/main/java/org/olat/modules/quality/generator/QualityGeneratorProvider.java b/src/main/java/org/olat/modules/quality/generator/QualityGeneratorProvider.java
index ffd0b973642..85cce02502d 100644
--- a/src/main/java/org/olat/modules/quality/generator/QualityGeneratorProvider.java
+++ b/src/main/java/org/olat/modules/quality/generator/QualityGeneratorProvider.java
@@ -26,9 +26,9 @@ import java.util.Locale;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.impl.Form;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.modules.quality.QualityDataCollection;
-import org.olat.modules.quality.generator.ui.GeneratorWhiteListController;
 import org.olat.modules.quality.generator.ui.ProviderConfigController;
 import org.olat.modules.quality.ui.security.GeneratorSecurityCallback;
 
@@ -52,7 +52,13 @@ public interface QualityGeneratorProvider {
 
 	public boolean hasWhiteListController();
 
-	public GeneratorWhiteListController getWhiteListController(UserRequest ureq, WindowControl wControl,
+	public Controller getWhiteListController(UserRequest ureq, WindowControl wControl,
+			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
+			QualityGeneratorConfigs configs);
+	
+	public boolean hasBlackListController();
+
+	public Controller getBlackListController(UserRequest ureq, WindowControl wControl,
 			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
 			QualityGeneratorConfigs configs);
 
diff --git a/src/main/java/org/olat/modules/quality/generator/QualityGeneratorService.java b/src/main/java/org/olat/modules/quality/generator/QualityGeneratorService.java
index b0467eeb65d..0319f2a8f76 100644
--- a/src/main/java/org/olat/modules/quality/generator/QualityGeneratorService.java
+++ b/src/main/java/org/olat/modules/quality/generator/QualityGeneratorService.java
@@ -27,10 +27,10 @@ import java.util.Locale;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.impl.Form;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.id.Organisation;
 import org.olat.modules.quality.QualityGeneratorProviderReferenceable;
-import org.olat.modules.quality.generator.ui.GeneratorWhiteListController;
 import org.olat.modules.quality.generator.ui.ProviderConfigController;
 import org.olat.modules.quality.ui.security.GeneratorSecurityCallback;
 
@@ -69,7 +69,12 @@ public interface QualityGeneratorService {
 
 	public boolean hasWhiteListController(QualityGenerator generator);
 	
-	public GeneratorWhiteListController getWhiteListController(UserRequest ureq, WindowControl wControl,
+	public Controller getWhiteListController(UserRequest ureq, WindowControl wControl,
+			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator);
+	
+	public boolean hasBlackListController(QualityGenerator generator);
+	
+	public Controller getBlackListController(UserRequest ureq, WindowControl wControl,
 			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator);
 
 	public void generateDataCollections();
diff --git a/src/main/java/org/olat/modules/quality/generator/manager/QualityGeneratorServiceImpl.java b/src/main/java/org/olat/modules/quality/generator/manager/QualityGeneratorServiceImpl.java
index 114de2f24d7..6a41bed7af9 100644
--- a/src/main/java/org/olat/modules/quality/generator/manager/QualityGeneratorServiceImpl.java
+++ b/src/main/java/org/olat/modules/quality/generator/manager/QualityGeneratorServiceImpl.java
@@ -30,12 +30,13 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Set;
 
+import org.apache.logging.log4j.Logger;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.impl.Form;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.id.Organisation;
-import org.apache.logging.log4j.Logger;
 import org.olat.core.logging.Tracing;
 import org.olat.modules.quality.QualityDataCollection;
 import org.olat.modules.quality.QualityGeneratorProviderReferenceable;
@@ -50,7 +51,6 @@ import org.olat.modules.quality.generator.QualityGeneratorSearchParams;
 import org.olat.modules.quality.generator.QualityGeneratorService;
 import org.olat.modules.quality.generator.QualityGeneratorToOrganisation;
 import org.olat.modules.quality.generator.QualityGeneratorView;
-import org.olat.modules.quality.generator.ui.GeneratorWhiteListController;
 import org.olat.modules.quality.generator.ui.ProviderConfigController;
 import org.olat.modules.quality.ui.security.GeneratorSecurityCallback;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -177,13 +177,27 @@ public class QualityGeneratorServiceImpl implements QualityGeneratorService {
 	}
 
 	@Override
-	public GeneratorWhiteListController getWhiteListController(UserRequest ureq, WindowControl wControl,
+	public Controller getWhiteListController(UserRequest ureq, WindowControl wControl,
 			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator) {
 		QualityGeneratorProvider provider = providerFactory.getProvider(generator.getType());
 		QualityGeneratorConfigsImpl configs = new QualityGeneratorConfigsImpl(generator);
 		return provider.getWhiteListController(ureq, wControl, secCallback, stackPanel, generator, configs);
 	}
 
+	@Override
+	public boolean hasBlackListController(QualityGenerator generator) {
+		QualityGeneratorProvider provider = providerFactory.getProvider(generator.getType());
+		return provider.hasBlackListController();
+	}
+
+	@Override
+	public Controller getBlackListController(UserRequest ureq, WindowControl wControl,
+			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator) {
+		QualityGeneratorProvider provider = providerFactory.getProvider(generator.getType());
+		QualityGeneratorConfigsImpl configs = new QualityGeneratorConfigsImpl(generator);
+		return provider.getBlackListController(ureq, wControl, secCallback, stackPanel, generator, configs);
+	}
+
 	@Override
 	public void generateDataCollections() {
 		List<QualityGenerator> generators = generatorDao.loadEnabledGenerators();
diff --git a/src/main/java/org/olat/modules/quality/generator/provider/course/CourseProvider.java b/src/main/java/org/olat/modules/quality/generator/provider/course/CourseProvider.java
index 9ad85b0346a..7c843b9d1bb 100644
--- a/src/main/java/org/olat/modules/quality/generator/provider/course/CourseProvider.java
+++ b/src/main/java/org/olat/modules/quality/generator/provider/course/CourseProvider.java
@@ -39,6 +39,7 @@ import org.olat.basesecurity.GroupRoles;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.impl.Form;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
@@ -62,8 +63,8 @@ import org.olat.modules.quality.generator.TitleCreator;
 import org.olat.modules.quality.generator.provider.course.manager.CourseProviderDAO;
 import org.olat.modules.quality.generator.provider.course.manager.SearchParameters;
 import org.olat.modules.quality.generator.provider.course.ui.CourseProviderConfigController;
-import org.olat.modules.quality.generator.ui.GeneratorWhiteListController;
 import org.olat.modules.quality.generator.ui.ProviderConfigController;
+import org.olat.modules.quality.generator.ui.RepositoryEntryBlackListController;
 import org.olat.modules.quality.generator.ui.RepositoryEntryWhiteListController;
 import org.olat.modules.quality.ui.security.GeneratorSecurityCallback;
 import org.olat.repository.RepositoryEntry;
@@ -162,12 +163,24 @@ public class CourseProvider implements QualityGeneratorProvider {
 	}
 
 	@Override
-	public GeneratorWhiteListController getWhiteListController(UserRequest ureq, WindowControl wControl,
+	public Controller getWhiteListController(UserRequest ureq, WindowControl wControl,
 			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
 			QualityGeneratorConfigs configs) {
 		return new RepositoryEntryWhiteListController(ureq, wControl, stackPanel, configs);
 	}
 
+	@Override
+	public boolean hasBlackListController() {
+		return true;
+	}
+
+	@Override
+	public Controller getBlackListController(UserRequest ureq, WindowControl wControl,
+			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
+			QualityGeneratorConfigs configs) {
+		return new RepositoryEntryBlackListController(ureq, wControl, stackPanel, configs);
+	}
+
 	@Override
 	public List<QualityDataCollection> generate(QualityGenerator generator, QualityGeneratorConfigs configs, Date fromDate, Date toDate) {
 		List<Organisation> organisations = generatorService.loadGeneratorOrganisations(generator);
@@ -344,8 +357,10 @@ public class CourseProvider implements QualityGeneratorProvider {
 		SearchParameters searchParams = new SearchParameters();
 		searchParams.setGeneratorRef(generator);
 		searchParams.setOrganisationRefs(organisations);
-		List<RepositoryEntryRef> repositoryEntryRefs = RepositoryEntryWhiteListController.getRepositoryEntryRefs(configs);
-		searchParams.setRepositoryEntryRefs(repositoryEntryRefs);
+		List<RepositoryEntryRef> whiteListRefs = RepositoryEntryWhiteListController.getRepositoryEntryRefs(configs);
+		searchParams.setWhiteListRefs(whiteListRefs);
+		List<RepositoryEntryRef> blackListRefs = RepositoryEntryBlackListController.getRepositoryEntryRefs(configs);
+		searchParams.setBlackListRefs(blackListRefs);
 		return searchParams;
 	}
 	
diff --git a/src/main/java/org/olat/modules/quality/generator/provider/course/manager/CourseProviderDAO.java b/src/main/java/org/olat/modules/quality/generator/provider/course/manager/CourseProviderDAO.java
index 82e14f80773..b4f60660abb 100644
--- a/src/main/java/org/olat/modules/quality/generator/provider/course/manager/CourseProviderDAO.java
+++ b/src/main/java/org/olat/modules/quality/generator/provider/course/manager/CourseProviderDAO.java
@@ -119,8 +119,11 @@ public class CourseProviderDAO {
 			sb.and();
 			sb.append("(lifecycle.validTo >= :validAt or lifecycle.validTo is null)");
 		}
-		if (searchParams.getRepositoryEntryRefs() != null && !searchParams.getRepositoryEntryRefs().isEmpty()) {
-			sb.and().append("entry.key in :repositoryKeys");
+		if (searchParams.getWhiteListRefs() != null && !searchParams.getWhiteListRefs().isEmpty()) {
+			sb.and().append("entry.key in (:whiteListKeys)");
+		}
+		if (searchParams.getBlackListRefs() != null && !searchParams.getBlackListRefs().isEmpty()) {
+			sb.and().append("entry.key not in (:blackListKeys)");
 		}
 	}
 
@@ -154,9 +157,13 @@ public class CourseProviderDAO {
 		if (searchParams.getLifecycleValidAt() != null) {
 			query.setParameter("validAt", searchParams.getLifecycleValidAt());
 		}
-		if (searchParams.getRepositoryEntryRefs() != null && !searchParams.getRepositoryEntryRefs().isEmpty()) {
-			List<Long> keys = searchParams.getRepositoryEntryRefs().stream().map(RepositoryEntryRef::getKey).collect(toList());
-			query.setParameter("repositoryKeys", keys);
+		if (searchParams.getWhiteListRefs() != null && !searchParams.getWhiteListRefs().isEmpty()) {
+			List<Long> keys = searchParams.getWhiteListRefs().stream().map(RepositoryEntryRef::getKey).collect(toList());
+			query.setParameter("whiteListKeys", keys);
+		}
+		if (searchParams.getBlackListRefs() != null && !searchParams.getBlackListRefs().isEmpty()) {
+			List<Long> keys = searchParams.getBlackListRefs().stream().map(RepositoryEntryRef::getKey).collect(toList());
+			query.setParameter("blackListKeys", keys);
 		}
 	}
 
diff --git a/src/main/java/org/olat/modules/quality/generator/provider/course/manager/SearchParameters.java b/src/main/java/org/olat/modules/quality/generator/provider/course/manager/SearchParameters.java
index f29cb519ee6..bb67c0e4837 100644
--- a/src/main/java/org/olat/modules/quality/generator/provider/course/manager/SearchParameters.java
+++ b/src/main/java/org/olat/modules/quality/generator/provider/course/manager/SearchParameters.java
@@ -44,7 +44,8 @@ public class SearchParameters {
 	private Date endFrom;
 	private Date endTo;
 	private Date lifecycleValidAt;
-	private Collection<? extends RepositoryEntryRef> repositoryEntryRefs;
+	private Collection<? extends RepositoryEntryRef> whiteListRefs;
+	private Collection<? extends RepositoryEntryRef> blackListRefs;
 	
 	public QualityGeneratorRef getGeneratorRef() {
 		return generatorRef;
@@ -53,6 +54,14 @@ public class SearchParameters {
 	public void setGeneratorRef(QualityGeneratorRef generatorRef) {
 		this.generatorRef = generatorRef;
 	}
+	
+	public Date getGeneratorDataCollectionStart() {
+		return generatorDataCollectionStart;
+	}
+
+	public void setGeneratorDataCollectionStart(Date generatorDataCollectionStart) {
+		this.generatorDataCollectionStart = generatorDataCollectionStart;
+	}
 
 	public List<? extends OrganisationRef> getOrganisationRefs() {
 		return organisationRefs;
@@ -101,23 +110,23 @@ public class SearchParameters {
 	public void setLifecycleValidAt(Date lifecycleValidAt) {
 		this.lifecycleValidAt = lifecycleValidAt;
 	}
-	
-	public Collection<? extends RepositoryEntryRef> getRepositoryEntryRefs() {
-		return repositoryEntryRefs;
+
+	public Collection<? extends RepositoryEntryRef> getWhiteListRefs() {
+		return whiteListRefs;
 	}
 	
-	public void setRepositoryEntryRefs(Collection<? extends RepositoryEntryRef> repositoryEntryRefs) {
-		this.repositoryEntryRefs = repositoryEntryRefs;
+	public void setWhiteListRefs(Collection<? extends RepositoryEntryRef> whiteListRefs) {
+		this.whiteListRefs = whiteListRefs;
 	}
 	
-	public Date getGeneratorDataCollectionStart() {
-		return generatorDataCollectionStart;
+	public Collection<? extends RepositoryEntryRef> getBlackListRefs() {
+		return blackListRefs;
 	}
-
-	public void setGeneratorDataCollectionStart(Date generatorDataCollectionStart) {
-		this.generatorDataCollectionStart = generatorDataCollectionStart;
+	
+	public void setBlackListRefs(Collection<? extends RepositoryEntryRef> blackListRefs) {
+		this.blackListRefs = blackListRefs;
 	}
-
+	
 	@Override
 	public String toString() {
 		StringBuilder builder = new StringBuilder();
@@ -137,8 +146,18 @@ public class SearchParameters {
 		builder.append(endFrom);
 		builder.append(", endTo=");
 		builder.append(endTo);
-		builder.append(", repositoryEntryRefs=");
-		builder.append(repositoryEntryRefs);
+		builder.append(", whiteListRefs (keys)=[");
+		builder.append(whiteListRefs.stream()
+				.map(RepositoryEntryRef::getKey)
+				.map(r -> r.toString())
+				.collect(Collectors.joining(", ")));
+		builder.append("]");
+		builder.append(", blackListRefs (keys)=[");
+		builder.append(blackListRefs.stream()
+				.map(RepositoryEntryRef::getKey)
+				.map(r -> r.toString())
+				.collect(Collectors.joining(", ")));
+		builder.append("]");
 		builder.append(", generatorDataCollectionStart=");
 		builder.append(generatorDataCollectionStart);
 		builder.append("]");
diff --git a/src/main/java/org/olat/modules/quality/generator/provider/courselectures/CourseLecturesFollowUpProvider.java b/src/main/java/org/olat/modules/quality/generator/provider/courselectures/CourseLecturesFollowUpProvider.java
index 0f5a3370776..7b39cecea07 100644
--- a/src/main/java/org/olat/modules/quality/generator/provider/courselectures/CourseLecturesFollowUpProvider.java
+++ b/src/main/java/org/olat/modules/quality/generator/provider/courselectures/CourseLecturesFollowUpProvider.java
@@ -37,6 +37,7 @@ import org.olat.basesecurity.GroupRoles;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.impl.Form;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
@@ -70,7 +71,6 @@ import org.olat.modules.quality.generator.provider.courselectures.manager.Course
 import org.olat.modules.quality.generator.provider.courselectures.manager.LectureBlockInfo;
 import org.olat.modules.quality.generator.provider.courselectures.manager.SearchParameters;
 import org.olat.modules.quality.generator.provider.courselectures.ui.CourseLectureFollowUpProviderConfigController;
-import org.olat.modules.quality.generator.ui.GeneratorWhiteListController;
 import org.olat.modules.quality.generator.ui.ProviderConfigController;
 import org.olat.modules.quality.ui.security.GeneratorSecurityCallback;
 import org.olat.repository.RepositoryEntry;
@@ -161,7 +161,19 @@ public class CourseLecturesFollowUpProvider implements QualityGeneratorProvider
 	}
 
 	@Override
-	public GeneratorWhiteListController getWhiteListController(UserRequest ureq, WindowControl wControl,
+	public Controller getWhiteListController(UserRequest ureq, WindowControl wControl,
+			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
+			QualityGeneratorConfigs configs) {
+		return null;
+	}
+
+	@Override
+	public boolean hasBlackListController() {
+		return false;
+	}
+
+	@Override
+	public Controller getBlackListController(UserRequest ureq, WindowControl wControl,
 			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
 			QualityGeneratorConfigs configs) {
 		return null;
diff --git a/src/main/java/org/olat/modules/quality/generator/provider/courselectures/CourseLecturesProvider.java b/src/main/java/org/olat/modules/quality/generator/provider/courselectures/CourseLecturesProvider.java
index 787136f4491..6bca758878f 100644
--- a/src/main/java/org/olat/modules/quality/generator/provider/courselectures/CourseLecturesProvider.java
+++ b/src/main/java/org/olat/modules/quality/generator/provider/courselectures/CourseLecturesProvider.java
@@ -37,6 +37,7 @@ import org.olat.basesecurity.GroupRoles;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.impl.Form;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
@@ -61,8 +62,8 @@ import org.olat.modules.quality.generator.provider.courselectures.manager.Course
 import org.olat.modules.quality.generator.provider.courselectures.manager.LectureBlockInfo;
 import org.olat.modules.quality.generator.provider.courselectures.manager.SearchParameters;
 import org.olat.modules.quality.generator.provider.courselectures.ui.CourseLectureProviderConfigController;
+import org.olat.modules.quality.generator.ui.CurriculumElementBlackListController;
 import org.olat.modules.quality.generator.ui.CurriculumElementWhiteListController;
-import org.olat.modules.quality.generator.ui.GeneratorWhiteListController;
 import org.olat.modules.quality.generator.ui.ProviderConfigController;
 import org.olat.modules.quality.ui.security.GeneratorSecurityCallback;
 import org.olat.repository.RepositoryEntry;
@@ -159,12 +160,24 @@ public class CourseLecturesProvider implements QualityGeneratorProvider {
 	}
 
 	@Override
-	public GeneratorWhiteListController getWhiteListController(UserRequest ureq, WindowControl wControl,
+	public Controller getWhiteListController(UserRequest ureq, WindowControl wControl,
 			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
 			QualityGeneratorConfigs configs) {
 		return new CurriculumElementWhiteListController(ureq, wControl, stackPanel, generator, configs);
 	}
 
+	@Override
+	public boolean hasBlackListController() {
+		return true;
+	}
+
+	@Override
+	public Controller getBlackListController(UserRequest ureq, WindowControl wControl,
+			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
+			QualityGeneratorConfigs configs) {
+		return new CurriculumElementBlackListController(ureq, wControl, stackPanel, generator, configs);
+	}
+
 	@Override
 	public List<QualityDataCollection> generate(QualityGenerator generator, QualityGeneratorConfigs configs,
 			Date fromDate, Date toDate) {
@@ -311,7 +324,7 @@ public class CourseLecturesProvider implements QualityGeneratorProvider {
 		searchParams.setTo(to);
 		
 		Collection<CurriculumElementRef> curriculumElementRefs = CurriculumElementWhiteListController.getCurriculumElementRefs(configs);
-		searchParams.setCurriculumElementRefs(curriculumElementRefs);
+		searchParams.setWhiteListRefs(curriculumElementRefs);
 		
 		String minLectures = configs.getValue(CONFIG_KEY_TOTAL_LECTURES_MIN);
 		if (StringHelper.containsNonWhitespace(minLectures)) {
diff --git a/src/main/java/org/olat/modules/quality/generator/provider/courselectures/manager/CourseLecturesProviderDAO.java b/src/main/java/org/olat/modules/quality/generator/provider/courselectures/manager/CourseLecturesProviderDAO.java
index 5c19db727b8..1e5187d1638 100644
--- a/src/main/java/org/olat/modules/quality/generator/provider/courselectures/manager/CourseLecturesProviderDAO.java
+++ b/src/main/java/org/olat/modules/quality/generator/provider/courselectures/manager/CourseLecturesProviderDAO.java
@@ -165,7 +165,7 @@ public class CourseLecturesProviderDAO {
 		if (!searchParams.getCourseRefs().isEmpty()) {
 			sb.and().append("lb.fk_entry in :courseKeys");
 		}
-		if (!searchParams.getCurriculumElementRefs().isEmpty()) {
+		if (!searchParams.getWhiteListRefs().isEmpty()) {
 			sb.and();
 			sb.append("lb.fk_entry in (");
 			sb.append("    select distinct v.repositoryentry_id");
@@ -174,7 +174,19 @@ public class CourseLecturesProviderDAO {
 			sb.append("             on rel.fk_entry_id = v.repositoryentry_id");
 			sb.append("           inner join o_cur_curriculum_element el");
 			sb.append("             on el.fk_group = rel.fk_group_id");
-			sb.append("     where el.id in :curriculumElementKeys");
+			sb.append("     where el.id in (:whiteListKeys)");
+			sb.append("    )");
+		}
+		if (!searchParams.getBlackListRefs().isEmpty()) {
+			sb.and();
+			sb.append("lb.fk_entry not in (");
+			sb.append("    select distinct v.repositoryentry_id");
+			sb.append("      from o_repositoryentry v");
+			sb.append("           inner join o_re_to_group rel");
+			sb.append("             on rel.fk_entry_id = v.repositoryentry_id");
+			sb.append("           inner join o_cur_curriculum_element el");
+			sb.append("             on el.fk_group = rel.fk_group_id");
+			sb.append("     where el.id in (:blackListKeys)");
 			sb.append("    )");
 		}
 		if (searchParams.getFinishedDataCollectionForGeneratorAndTopicIdentityRef() != null) {
@@ -386,14 +398,24 @@ public class CourseLecturesProviderDAO {
 		if (!searchParams.getCourseRefs().isEmpty()) {
 			sb.and().append("course.key in :courseKeys");
 		}
-		if (!searchParams.getCurriculumElementRefs().isEmpty()) {
+		if (!searchParams.getWhiteListRefs().isEmpty()) {
 			sb.and();
 			sb.append("course.key in (");
 			sb.append("    select distinct v.key");
 			sb.append("      from repositoryentry as v");
 			sb.append("           inner join v.groups as rel");
 			sb.append("           inner join curriculumelement as el on (el.group.key=rel.group.key)");
-			sb.append("     where el.key in :curriculumElementKeys");
+			sb.append("     where el.key in (:whiteListKeys)");
+			sb.append("    )");
+		}
+		if (!searchParams.getBlackListRefs().isEmpty()) {
+			sb.and();
+			sb.append("course.key not in (");
+			sb.append("    select distinct v.key");
+			sb.append("      from repositoryentry as v");
+			sb.append("           inner join v.groups as rel");
+			sb.append("           inner join curriculumelement as el on (el.group.key=rel.group.key)");
+			sb.append("     where el.key in (:blackListKeys)");
 			sb.append("    )");
 		}
 		if (!searchParams.getOrganisationRefs().isEmpty()) {
@@ -443,9 +465,13 @@ public class CourseLecturesProviderDAO {
 			List<Long> courseKeys = searchParams.getCourseRefs().stream().map(RepositoryEntryRef::getKey).collect(Collectors.toList());
 			query.setParameter("courseKeys", courseKeys);
 		}
-		if (!searchParams.getCurriculumElementRefs().isEmpty()) {
-			List<Long> curriculumElementKeys = searchParams.getCurriculumElementRefs().stream().map(CurriculumElementRef::getKey).collect(Collectors.toList());
-			query.setParameter("curriculumElementKeys", curriculumElementKeys);
+		if (!searchParams.getWhiteListRefs().isEmpty()) {
+			List<Long> curriculumElementKeys = searchParams.getWhiteListRefs().stream().map(CurriculumElementRef::getKey).collect(Collectors.toList());
+			query.setParameter("whiteListKeys", curriculumElementKeys);
+		}
+		if (!searchParams.getBlackListRefs().isEmpty()) {
+			List<Long> curriculumElementKeys = searchParams.getBlackListRefs().stream().map(CurriculumElementRef::getKey).collect(Collectors.toList());
+			query.setParameter("blackListKeys", curriculumElementKeys);
 		}
 		if (!searchParams.getOrganisationRefs().isEmpty()) {
 			for (int i = 0; i < searchParams.getOrganisationRefs().size(); i++) {
diff --git a/src/main/java/org/olat/modules/quality/generator/provider/courselectures/manager/SearchParameters.java b/src/main/java/org/olat/modules/quality/generator/provider/courselectures/manager/SearchParameters.java
index 1cca0393d98..f4db3dc9e10 100644
--- a/src/main/java/org/olat/modules/quality/generator/provider/courselectures/manager/SearchParameters.java
+++ b/src/main/java/org/olat/modules/quality/generator/provider/courselectures/manager/SearchParameters.java
@@ -50,7 +50,8 @@ public class SearchParameters {
 	private QualityGeneratorRef excludeGeneratorAndTopicRepositoryRef;
 	private IdentityRef teacherRef;
 	private Collection<? extends RepositoryEntryRef> courseRefs;
-	private Collection<? extends CurriculumElementRef> curriculumElementRefs;
+	private Collection<? extends CurriculumElementRef> whiteListRefs;
+	private Collection<? extends CurriculumElementRef> blackListRefs;
 	private List<? extends OrganisationRef> organisationRefs;
 	private Date from;
 	private Date to;
@@ -138,15 +139,26 @@ public class SearchParameters {
 		this.courseRefs = courseRefs;
 	}
 
-	public Collection<? extends CurriculumElementRef> getCurriculumElementRefs() {
-		if (curriculumElementRefs == null) {
-			curriculumElementRefs = Collections.emptyList();
+	public Collection<? extends CurriculumElementRef> getWhiteListRefs() {
+		if (whiteListRefs == null) {
+			whiteListRefs = Collections.emptyList();
 		}
-		return curriculumElementRefs;
+		return whiteListRefs;
 	}
 
-	public void setCurriculumElementRefs(Collection<? extends CurriculumElementRef> curriculumElementRefs) {
-		this.curriculumElementRefs = curriculumElementRefs;
+	public void setWhiteListRefs(Collection<? extends CurriculumElementRef> whiteListRefs) {
+		this.whiteListRefs = whiteListRefs;
+	}
+
+	public Collection<? extends CurriculumElementRef> getBlackListRefs() {
+		if (blackListRefs == null) {
+			blackListRefs = Collections.emptyList();
+		}
+		return blackListRefs;
+	}
+
+	public void setBlackListRefs(Collection<? extends CurriculumElementRef> blackListRefs) {
+		this.blackListRefs = blackListRefs;
 	}
 
 	public List<? extends OrganisationRef> getOrganisationRefs() {
@@ -200,7 +212,7 @@ public class SearchParameters {
 		builder.append(", courseRefs=");
 		builder.append(courseRefs);
 		builder.append(", curriculumElementRefs={");
-		builder.append(curriculumElementRefs.stream()
+		builder.append(whiteListRefs.stream()
 				.map(CurriculumElementRef::getKey)
 				.map(k -> k.toString())
 				.collect(Collectors.joining(", ")));
diff --git a/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/CurriculumElementProvider.java b/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/CurriculumElementProvider.java
index ade5a38b510..acf4381ed3d 100644
--- a/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/CurriculumElementProvider.java
+++ b/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/CurriculumElementProvider.java
@@ -33,6 +33,7 @@ import org.apache.logging.log4j.Logger;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.impl.Form;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
@@ -58,8 +59,8 @@ import org.olat.modules.quality.generator.TitleCreator;
 import org.olat.modules.quality.generator.provider.curriculumelement.manager.CurriculumElementProviderDAO;
 import org.olat.modules.quality.generator.provider.curriculumelement.manager.SearchParameters;
 import org.olat.modules.quality.generator.provider.curriculumelement.ui.CurriculumElementProviderConfigController;
+import org.olat.modules.quality.generator.ui.CurriculumElementBlackListController;
 import org.olat.modules.quality.generator.ui.CurriculumElementWhiteListController;
-import org.olat.modules.quality.generator.ui.GeneratorWhiteListController;
 import org.olat.modules.quality.generator.ui.ProviderConfigController;
 import org.olat.modules.quality.ui.security.GeneratorSecurityCallback;
 import org.olat.repository.RepositoryEntry;
@@ -136,12 +137,24 @@ public class CurriculumElementProvider implements QualityGeneratorProvider {
 	}
 
 	@Override
-	public GeneratorWhiteListController getWhiteListController(UserRequest ureq, WindowControl wControl,
+	public Controller getWhiteListController(UserRequest ureq, WindowControl wControl,
 			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
 			QualityGeneratorConfigs configs) {
 		return new CurriculumElementWhiteListController(ureq, wControl, stackPanel, generator, configs);
 	}
 
+	@Override
+	public boolean hasBlackListController() {
+		return true;
+	}
+
+	@Override
+	public Controller getBlackListController(UserRequest ureq, WindowControl wControl,
+			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
+			QualityGeneratorConfigs configs) {
+		return new CurriculumElementBlackListController(ureq, wControl, stackPanel, generator, configs);
+	}
+
 	@Override
 	public List<QualityDataCollection> generate(QualityGenerator generator, QualityGeneratorConfigs configs,
 			Date fromDate, Date toDate) {
@@ -255,8 +268,10 @@ public class CurriculumElementProvider implements QualityGeneratorProvider {
 			searchParams.setStartDate(false);
 		}
 		
-		List<CurriculumElementRef> curriculumElementRefs = CurriculumElementWhiteListController.getCurriculumElementRefs(configs);
-		searchParams.setCurriculumElementRefs(curriculumElementRefs);
+		List<CurriculumElementRef> whiteListRefs = CurriculumElementWhiteListController.getCurriculumElementRefs(configs);
+		searchParams.setWhiteListRefs(whiteListRefs);
+		List<CurriculumElementRef> blackListRefs = CurriculumElementBlackListController.getCurriculumElementRefs(configs);
+		searchParams.setBlackListRefs(blackListRefs);
 		
 		return searchParams;
 	}
diff --git a/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/CurriculumElementProviderDAO.java b/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/CurriculumElementProviderDAO.java
index e20c2a209d8..cffcef81e34 100644
--- a/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/CurriculumElementProviderDAO.java
+++ b/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/CurriculumElementProviderDAO.java
@@ -98,8 +98,11 @@ public class CurriculumElementProviderDAO {
 				}
 			}
 		}
-		if (searchParams.getCurriculumElementRefs() != null && !searchParams.getCurriculumElementRefs().isEmpty()) {
-			sb.and().append("curEle.key in :curEleKeys");
+		if (searchParams.getWhiteListRefs() != null && !searchParams.getWhiteListRefs().isEmpty()) {
+			sb.and().append("curEle.key in (:whiteListKeys)");
+		}
+		if (searchParams.getBlackListRefs() != null && !searchParams.getBlackListRefs().isEmpty()) {
+			sb.and().append("curEle.key not in (:blackListKeys)");
 		}
 	}
 	
@@ -130,10 +133,15 @@ public class CurriculumElementProviderDAO {
 				query.setParameter(parameter, value);
 			}
 		}
-		if (searchParams.getCurriculumElementRefs() != null && !searchParams.getCurriculumElementRefs().isEmpty()) {
-			List<Long> curEleKeys = searchParams.getCurriculumElementRefs().stream().map(CurriculumElementRef::getKey).collect(toList());
-			query.setParameter("curEleKeys", curEleKeys);
+		if (searchParams.getWhiteListRefs() != null && !searchParams.getWhiteListRefs().isEmpty()) {
+			List<Long> curEleKeys = searchParams.getWhiteListRefs().stream().map(CurriculumElementRef::getKey).collect(toList());
+			query.setParameter("whiteListKeys", curEleKeys);
+		}
+		if (searchParams.getBlackListRefs() != null && !searchParams.getBlackListRefs().isEmpty()) {
+			List<Long> curEleKeys = searchParams.getBlackListRefs().stream().map(CurriculumElementRef::getKey).collect(toList());
+			query.setParameter("blackListKeys", curEleKeys);
 		}
+
 	}
 
 }
diff --git a/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/SearchParameters.java b/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/SearchParameters.java
index 3bf4a96d8fb..454367a7e82 100644
--- a/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/SearchParameters.java
+++ b/src/main/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/SearchParameters.java
@@ -39,7 +39,8 @@ public class SearchParameters {
 	private QualityGeneratorRef generatorRef;
 	private List<? extends OrganisationRef> organisationRefs;
 	private Long ceTypeKey;
-	private Collection<? extends CurriculumElementRef> curriculumElementRefs;
+	private Collection<? extends CurriculumElementRef> whiteListRefs;
+	private Collection<? extends CurriculumElementRef> blackListRefs;
 	private Date from;
 	private Date to;
 	private boolean startDate;
@@ -60,12 +61,20 @@ public class SearchParameters {
 		this.organisationRefs = organisations;
 	}
 
-	public Collection<? extends CurriculumElementRef> getCurriculumElementRefs() {
-		return curriculumElementRefs;
+	public Collection<? extends CurriculumElementRef> getWhiteListRefs() {
+		return whiteListRefs;
 	}
 
-	public void setCurriculumElementRefs(Collection<? extends CurriculumElementRef> curriculumElementRefs) {
-		this.curriculumElementRefs = curriculumElementRefs;
+	public void setWhiteListRefs(Collection<? extends CurriculumElementRef> whiteListRefs) {
+		this.whiteListRefs = whiteListRefs;
+	}
+
+	public Collection<? extends CurriculumElementRef> getBlackListRefs() {
+		return blackListRefs;
+	}
+
+	public void setBlackListRefs(Collection<? extends CurriculumElementRef> blackListRefs) {
+		this.blackListRefs = blackListRefs;
 	}
 
 	public Long getCeTypeKey() {
@@ -111,8 +120,14 @@ public class SearchParameters {
 		builder.append("]");
 		builder.append(", ceTypeKey=");
 		builder.append(ceTypeKey);
-		builder.append(", curriculumElementRefs=");
-		builder.append(curriculumElementRefs);
+		builder.append(", whiteListRefs (keys)=[");
+		builder.append(whiteListRefs.stream().map(CurriculumElementRef::getKey).map(ce -> ce.toString())
+				.collect(Collectors.joining(", ")));
+		builder.append("]");
+		builder.append(", blackListRefs (keys)=[");
+		builder.append(blackListRefs.stream().map(CurriculumElementRef::getKey).map(ce -> ce.toString())
+				.collect(Collectors.joining(", ")));
+		builder.append("]");
 		builder.append(", from=");
 		builder.append(from);
 		builder.append(", to=");
diff --git a/src/main/java/org/olat/modules/quality/generator/provider/fallback/FallbackProvider.java b/src/main/java/org/olat/modules/quality/generator/provider/fallback/FallbackProvider.java
index 77930dc1f39..67ab7bef5dd 100644
--- a/src/main/java/org/olat/modules/quality/generator/provider/fallback/FallbackProvider.java
+++ b/src/main/java/org/olat/modules/quality/generator/provider/fallback/FallbackProvider.java
@@ -27,6 +27,7 @@ import java.util.Locale;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.impl.Form;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.util.Util;
@@ -35,7 +36,6 @@ import org.olat.modules.quality.generator.QualityGenerator;
 import org.olat.modules.quality.generator.QualityGeneratorConfigs;
 import org.olat.modules.quality.generator.QualityGeneratorProvider;
 import org.olat.modules.quality.generator.ui.GeneratorListController;
-import org.olat.modules.quality.generator.ui.GeneratorWhiteListController;
 import org.olat.modules.quality.generator.ui.ProviderConfigController;
 import org.olat.modules.quality.ui.security.GeneratorSecurityCallback;
 import org.springframework.stereotype.Service;
@@ -79,7 +79,19 @@ public class FallbackProvider implements QualityGeneratorProvider {
 	}
 
 	@Override
-	public GeneratorWhiteListController getWhiteListController(UserRequest ureq, WindowControl wControl,
+	public Controller getWhiteListController(UserRequest ureq, WindowControl wControl,
+			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
+			QualityGeneratorConfigs configs) {
+		return null;
+	}
+
+	@Override
+	public boolean hasBlackListController() {
+		return false;
+	}
+
+	@Override
+	public Controller getBlackListController(UserRequest ureq, WindowControl wControl,
 			GeneratorSecurityCallback secCallback, TooledStackedPanel stackPanel, QualityGenerator generator,
 			QualityGeneratorConfigs configs) {
 		return null;
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementBlackListController.java b/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementBlackListController.java
new file mode 100644
index 00000000000..71e4392b541
--- /dev/null
+++ b/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementBlackListController.java
@@ -0,0 +1,65 @@
+/**
+ * <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.quality.generator.ui;
+
+import java.util.List;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.modules.curriculum.CurriculumElementRef;
+import org.olat.modules.quality.generator.QualityGenerator;
+import org.olat.modules.quality.generator.QualityGeneratorConfigs;
+
+/**
+ * 
+ * Initial date: 27 May 2020<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class CurriculumElementBlackListController extends CurriculumElementListController {
+
+	private static final String CONFIG_KEY = "curriculum.element.black.list";
+
+	public CurriculumElementBlackListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
+			QualityGenerator generator, QualityGeneratorConfigs configs) {
+		super(ureq, wControl, stackPanel, generator, configs);
+	}
+
+	@Override
+	protected String getConfigKey() {
+		return CONFIG_KEY;
+	}
+
+	@Override
+	protected String getTablePrefsKey() {
+		return "quality-ce-black-list";
+	}
+	
+	public static List<CurriculumElementRef> getCurriculumElementRefs(QualityGeneratorConfigs generatorConfigs) {
+		return CurriculumElementListController.getCurriculumElementRefs(generatorConfigs, CONFIG_KEY);
+	}
+	
+	public static void setCurriculumElementRefs(QualityGeneratorConfigs generatorConfigs,
+			List<? extends CurriculumElementRef> elements) {
+		CurriculumElementListController.setCurriculumElementRefs(generatorConfigs, elements, CONFIG_KEY);
+	}
+
+}
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementListController.java b/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementListController.java
new file mode 100644
index 00000000000..2065f9abf32
--- /dev/null
+++ b/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementListController.java
@@ -0,0 +1,289 @@
+/**
+ * <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.quality.generator.ui;
+
+import static java.util.stream.Collectors.joining;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.form.flexible.FormItem;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
+import org.olat.core.gui.components.form.flexible.elements.FormLink;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormEvent;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
+import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.components.link.LinkFactory;
+import org.olat.core.gui.components.stack.TooledController;
+import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.components.stack.TooledStackedPanel.Align;
+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.gui.control.generic.closablewrapper.CloseableModalController;
+import org.olat.core.id.Organisation;
+import org.olat.core.util.StringHelper;
+import org.olat.modules.curriculum.CurriculumElement;
+import org.olat.modules.curriculum.CurriculumElementRef;
+import org.olat.modules.curriculum.CurriculumService;
+import org.olat.modules.curriculum.model.CurriculumElementRefImpl;
+import org.olat.modules.quality.generator.QualityGenerator;
+import org.olat.modules.quality.generator.QualityGeneratorConfigs;
+import org.olat.modules.quality.generator.QualityGeneratorService;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 27 May 2020<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public abstract class CurriculumElementListController extends FormBasicController implements TooledController {
+
+	private static final String KEY_DELIMITER = ",";
+	
+	private FlexiTableElement tableEl;
+	private CurriculumElementListDataModel tableModel;
+	private Link addLink;
+	private FormLink removeLink;
+	
+	private CloseableModalController cmc;
+	private CurriculumElementSelectionController selectCtrl;
+	private CurriculumElementRemoveConfirmationController removeConfirmationCtrl;
+	
+	private final TooledStackedPanel stackPanel;
+	private final QualityGenerator generator;
+	private final QualityGeneratorConfigs configs;
+	
+	@Autowired
+	private QualityGeneratorService generatorService;
+	@Autowired
+	private CurriculumService curriculumService;
+
+	public CurriculumElementListController(UserRequest ureq, WindowControl wControl,
+			TooledStackedPanel stackPanel, QualityGenerator generator, QualityGeneratorConfigs configs) {
+		super(ureq, wControl, LAYOUT_BAREBONE);
+		this.stackPanel = stackPanel;
+		this.generator = generator;
+		this.configs = configs;
+		initForm(ureq);
+	}
+
+	protected abstract String getConfigKey();
+	
+	protected abstract String getTablePrefsKey();
+	
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(CurriculumElementListDataModel.Cols.displayName));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(CurriculumElementListDataModel.Cols.identifier));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(CurriculumElementListDataModel.Cols.typeName));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(CurriculumElementListDataModel.Cols.begin));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(CurriculumElementListDataModel.Cols.end));
+
+		tableModel = new CurriculumElementListDataModel(columnsModel, getLocale());
+		tableEl = uifactory.addTableElement(getWindowControl(), "table", tableModel, 20, true, getTranslator(), formLayout);
+		tableEl.setEmtpyTableMessageKey("curriculum.element.empty.table");
+		tableEl.setAndLoadPersistedPreferences(ureq, getTablePrefsKey());
+		tableEl.setMultiSelect(true);
+		tableEl.setSelectAllEnable(true);
+		
+		FormLayoutContainer buttons = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
+		formLayout.add("buttons", buttons);
+		buttons.setElementCssClass("o_button_group");
+		removeLink = uifactory.addFormLink("curriculum.element.remove", buttons, Link.BUTTON);
+		
+		loadModel();
+	}
+	
+	@Override
+	public void initTools() {
+		addLink = LinkFactory.createToolLink("curriculum.element.add", translate("curriculum.element.add"), this);
+		addLink.setIconLeftCSS("o_icon o_icon-fw o_icon_qual_gen_ce_add");
+		stackPanel.addTool(addLink, Align.right);
+	}
+
+	private void loadModel() {
+		List<CurriculumElementRef> elementRefs = getCurriculumElementRefs(configs, getConfigKey());
+		List<CurriculumElement> curriculumElements = curriculumService.getCurriculumElements(elementRefs);
+		tableModel.setObjects(curriculumElements);
+		tableEl.reset(true, true, true);
+		
+		removeLink.setVisible(!curriculumElements.isEmpty());
+		flc.setDirty(true);
+	}
+	
+	@Override
+	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
+		if (source == removeLink) {
+			List<CurriculumElement> elements = getSelectedCurriculumElements();
+			doConfirmRemove(ureq, elements);
+		}
+	}
+
+	private List<CurriculumElement> getSelectedCurriculumElements() {
+		return tableEl.getMultiSelectedIndex().stream()
+				.map(index -> tableModel.getObject(index.intValue()))
+				.collect(Collectors.toList());
+	}
+
+	@Override
+	public void event(UserRequest ureq, Component source, Event event) {
+		if (source == addLink) {
+			doSelectCurriculumElement(ureq);
+		} 
+		super.event(ureq, source, event);
+	}
+	
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		if (source == selectCtrl) {
+			if (Event.DONE_EVENT.equals(event)) {
+				String elementKey = selectCtrl.getCurriculumElementKey();
+				doAddCurriculumElement(elementKey);
+			}
+			cmc.deactivate();
+			cleanUp();
+		} else if (source == removeConfirmationCtrl) {
+			if (Event.DONE_EVENT.equals(event)) {
+				List<CurriculumElement> elements = removeConfirmationCtrl.getCurriculumElements();
+				doRemove(elements);
+			}
+			cmc.deactivate();
+			cleanUp();
+		} else if (source == cmc) {
+			cleanUp();
+		}
+		super.event(ureq, source, event);
+	}
+	
+	private void cleanUp() {
+		removeAsListenerAndDispose(removeConfirmationCtrl);
+		removeAsListenerAndDispose(selectCtrl);
+		removeAsListenerAndDispose(cmc);
+		removeConfirmationCtrl = null;
+		selectCtrl = null;
+		cmc = null;
+	}
+
+	protected static List<CurriculumElementRef> getCurriculumElementRefs(QualityGeneratorConfigs generatorConfigs,
+			String configKey) {
+		String whiteListConfig = generatorConfigs.getValue(configKey);
+		String[] keys = StringHelper.containsNonWhitespace(whiteListConfig)
+				? whiteListConfig.split(KEY_DELIMITER)
+				: new String[0];
+		List<CurriculumElementRef> elementRefs = Arrays.stream(keys)
+				.map(Long::valueOf)
+				.map(CurriculumElementRefImpl::new)
+				.collect(Collectors.toList());
+		return elementRefs;
+	}
+	
+	public static void setCurriculumElementRefs(QualityGeneratorConfigs generatorConfigs,
+			List<? extends CurriculumElementRef> elements, String configKey) {
+		for (CurriculumElementRef element : elements) {
+			doAddCurriculumElement(generatorConfigs, element.getKey().toString(), configKey);
+		}
+	}
+
+	private static void doAddCurriculumElement(QualityGeneratorConfigs generatorConfigs, String elementKey, String configKey) {
+		if (StringHelper.containsNonWhitespace(elementKey)) {
+			String whiteListConfig = generatorConfigs.getValue(configKey);
+			if (StringHelper.containsNonWhitespace(whiteListConfig)) {
+				String[] keys = whiteListConfig.split(KEY_DELIMITER);
+				if (!Arrays.asList(keys).contains(elementKey)) {
+					whiteListConfig += KEY_DELIMITER + elementKey;
+				}
+			} else {
+				whiteListConfig = elementKey;
+			}
+			generatorConfigs.setValue(configKey, whiteListConfig);
+		}
+	}
+
+	private void doSelectCurriculumElement(UserRequest ureq) {
+		List<Organisation> organisations = generatorService.loadGeneratorOrganisations(generator);
+		
+		selectCtrl = new CurriculumElementSelectionController(ureq, getWindowControl(), organisations);
+		listenTo(selectCtrl);
+
+		cmc = new CloseableModalController(getWindowControl(), translate("close"),
+				selectCtrl.getInitialComponent(), true, translate("curriculum.element.select.title"));
+		cmc.activate();
+		listenTo(cmc);
+	}
+
+	private void doAddCurriculumElement(String elementKey) {
+		doAddCurriculumElement(configs, elementKey, getConfigKey());
+		loadModel();
+	}
+
+	private void doConfirmRemove(UserRequest ureq, List<CurriculumElement> elements) {
+		if (elements.isEmpty()) {
+			showWarning("curriculum.element.none.selected");
+		} else {
+			removeConfirmationCtrl = new CurriculumElementRemoveConfirmationController(ureq, getWindowControl(), elements);
+			listenTo(removeConfirmationCtrl);
+			
+			cmc = new CloseableModalController(getWindowControl(), translate("close"),
+					removeConfirmationCtrl.getInitialComponent(), true, translate("curriculum.element.remove.confirm.title"));
+			cmc.activate();
+			listenTo(cmc);
+		}
+	}
+
+	private void doRemove(List<CurriculumElement> elements) {
+		List<String> keysToRemove = elements.stream()
+				.map(CurriculumElementRef::getKey)
+				.map(String::valueOf)
+				.collect(Collectors.toList());
+		
+		String whiteListConfig = configs.getValue(getConfigKey());
+		String[] splittedKeys = StringHelper.containsNonWhitespace(whiteListConfig)
+				? whiteListConfig.split(KEY_DELIMITER)
+				: new String[0];
+		List<String> currentKeys = Arrays.stream(splittedKeys).collect(Collectors.toList());
+		currentKeys.removeAll(keysToRemove);
+		
+		String keys = currentKeys.stream().collect(joining(KEY_DELIMITER));
+		configs.setValue(getConfigKey(), keys);
+		loadModel();
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+}
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementWhiteListDataModel.java b/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementListDataModel.java
similarity index 92%
rename from src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementWhiteListDataModel.java
rename to src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementListDataModel.java
index ffc98cb2f2b..36815adb8b2 100644
--- a/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementWhiteListDataModel.java
+++ b/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementListDataModel.java
@@ -37,12 +37,12 @@ import org.olat.modules.curriculum.CurriculumElementType;
  * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
  *
  */
-class CurriculumElementWhiteListDataModel extends DefaultFlexiTableDataModel<CurriculumElement>
+class CurriculumElementListDataModel extends DefaultFlexiTableDataModel<CurriculumElement>
 		implements SortableFlexiTableDataModel<CurriculumElement> {
 	
 	private final Locale locale;
 	
-	CurriculumElementWhiteListDataModel(FlexiTableColumnModel columnsModel, Locale locale) {
+	CurriculumElementListDataModel(FlexiTableColumnModel columnsModel, Locale locale) {
 		super(columnsModel);
 		this.locale = locale;
 	}
@@ -76,7 +76,7 @@ class CurriculumElementWhiteListDataModel extends DefaultFlexiTableDataModel<Cur
 
 	@Override
 	public DefaultFlexiTableDataModel<CurriculumElement> createCopyWithEmptyList() {
-		return new CurriculumElementWhiteListDataModel(getTableColumnModel(), locale);
+		return new CurriculumElementListDataModel(getTableColumnModel(), locale);
 	}
 	
 	public enum Cols implements FlexiSortableColumnDef {
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementWhiteListController.java b/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementWhiteListController.java
index 645050389de..3338df6c205 100644
--- a/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementWhiteListController.java
+++ b/src/main/java/org/olat/modules/quality/generator/ui/CurriculumElementWhiteListController.java
@@ -19,44 +19,14 @@
  */
 package org.olat.modules.quality.generator.ui;
 
-import static java.util.stream.Collectors.joining;
-
-import java.util.Arrays;
 import java.util.List;
-import java.util.stream.Collectors;
 
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.form.flexible.FormItem;
-import org.olat.core.gui.components.form.flexible.FormItemContainer;
-import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
-import org.olat.core.gui.components.form.flexible.elements.FormLink;
-import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
-import org.olat.core.gui.components.form.flexible.impl.FormEvent;
-import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
-import org.olat.core.gui.components.link.Link;
-import org.olat.core.gui.components.link.LinkFactory;
-import org.olat.core.gui.components.stack.TooledController;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
-import org.olat.core.gui.components.stack.TooledStackedPanel.Align;
-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.gui.control.generic.closablewrapper.CloseableModalController;
-import org.olat.core.id.Organisation;
-import org.olat.core.util.StringHelper;
-import org.olat.modules.curriculum.CurriculumElement;
 import org.olat.modules.curriculum.CurriculumElementRef;
-import org.olat.modules.curriculum.CurriculumService;
-import org.olat.modules.curriculum.model.CurriculumElementRefImpl;
 import org.olat.modules.quality.generator.QualityGenerator;
 import org.olat.modules.quality.generator.QualityGeneratorConfigs;
-import org.olat.modules.quality.generator.QualityGeneratorService;
-import org.olat.modules.quality.generator.ui.CurriculumElementWhiteListDataModel.Cols;
-import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * 
@@ -64,223 +34,32 @@ import org.springframework.beans.factory.annotation.Autowired;
  * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
  *
  */
-public class CurriculumElementWhiteListController extends FormBasicController
-implements GeneratorWhiteListController, TooledController {
+public class CurriculumElementWhiteListController extends CurriculumElementListController {
 
-	private static final String CURRICULUM_ELEMENT_WHITE_LIST = "curriculum.element.white.list";
-	private static final String KEY_DELIMITER = ",";
-	
-	private FlexiTableElement tableEl;
-	private CurriculumElementWhiteListDataModel tableModel;
-	private Link addLink;
-	private FormLink removeLink;
-	
-	private CloseableModalController cmc;
-	private CurriculumElementSelectionController selectCtrl;
-	private CurriculumElementRemoveConfirmationController removeConfirmationCtrl;
-	
-	private final TooledStackedPanel stackPanel;
-	private final QualityGenerator generator;
-	private final QualityGeneratorConfigs configs;
-	
-	@Autowired
-	private QualityGeneratorService generatorService;
-	@Autowired
-	private CurriculumService curriculumService;
+	private static final String CONFIG_KEY = "curriculum.element.white.list";
 
-	public CurriculumElementWhiteListController(UserRequest ureq, WindowControl wControl,
-			TooledStackedPanel stackPanel, QualityGenerator generator, QualityGeneratorConfigs configs) {
-		super(ureq, wControl, LAYOUT_BAREBONE);
-		this.stackPanel = stackPanel;
-		this.generator = generator;
-		this.configs = configs;
-		initForm(ureq);
+	public CurriculumElementWhiteListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
+			QualityGenerator generator, QualityGeneratorConfigs configs) {
+		super(ureq, wControl, stackPanel, generator, configs);
 	}
 
 	@Override
-	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
-		FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.displayName));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.identifier));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.typeName));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.begin));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.end));
-
-		tableModel = new CurriculumElementWhiteListDataModel(columnsModel, getLocale());
-		tableEl = uifactory.addTableElement(getWindowControl(), "table", tableModel, 20, true, getTranslator(), formLayout);
-		tableEl.setEmtpyTableMessageKey("curriculum.element.empty.table");
-		tableEl.setAndLoadPersistedPreferences(ureq, "quality-ce-white-list");
-		tableEl.setMultiSelect(true);
-		tableEl.setSelectAllEnable(true);
-		
-		FormLayoutContainer buttons = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
-		formLayout.add("buttons", buttons);
-		buttons.setElementCssClass("o_button_group");
-		removeLink = uifactory.addFormLink("curriculum.element.remove", buttons, Link.BUTTON);
-		
-		loadModel();
-	}
-	
-	@Override
-	public void initTools() {
-		addLink = LinkFactory.createToolLink("curriculum.element.add", translate("curriculum.element.add"), this);
-		addLink.setIconLeftCSS("o_icon o_icon-fw o_icon_qual_gen_ce_add");
-		stackPanel.addTool(addLink, Align.right);
+	protected String getConfigKey() {
+		return CONFIG_KEY;
 	}
 
-	private void loadModel() {
-		List<CurriculumElementRef> elementRefs = getCurriculumElementRefs(configs);
-		List<CurriculumElement> curriculumElements = curriculumService.getCurriculumElements(elementRefs);
-		tableModel.setObjects(curriculumElements);
-		tableEl.reset(true, true, true);
-		
-		removeLink.setVisible(!curriculumElements.isEmpty());
-		flc.setDirty(true);
-	}
-	
-	@Override
-	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
-		if (source == removeLink) {
-			List<CurriculumElement> elements = getSelectedCurriculumElements();
-			doConfirmRemove(ureq, elements);
-		}
-	}
-
-	private List<CurriculumElement> getSelectedCurriculumElements() {
-		return tableEl.getMultiSelectedIndex().stream()
-				.map(index -> tableModel.getObject(index.intValue()))
-				.collect(Collectors.toList());
-	}
-
-	@Override
-	public void event(UserRequest ureq, Component source, Event event) {
-		if (source == addLink) {
-			doSelectCurriculumElement(ureq);
-		} 
-		super.event(ureq, source, event);
-	}
-	
 	@Override
-	protected void event(UserRequest ureq, Controller source, Event event) {
-		if (source == selectCtrl) {
-			if (Event.DONE_EVENT.equals(event)) {
-				String elementKey = selectCtrl.getCurriculumElementKey();
-				doAddCurriculumElement(elementKey);
-			}
-			cmc.deactivate();
-			cleanUp();
-		} else if (source == removeConfirmationCtrl) {
-			if (Event.DONE_EVENT.equals(event)) {
-				List<CurriculumElement> elements = removeConfirmationCtrl.getCurriculumElements();
-				doRemove(elements);
-			}
-			cmc.deactivate();
-			cleanUp();
-		} else if (source == cmc) {
-			cleanUp();
-		}
-		super.event(ureq, source, event);
+	protected String getTablePrefsKey() {
+		return "quality-ce-white-list";
 	}
 	
-	private void cleanUp() {
-		removeAsListenerAndDispose(removeConfirmationCtrl);
-		removeAsListenerAndDispose(selectCtrl);
-		removeAsListenerAndDispose(cmc);
-		removeConfirmationCtrl = null;
-		selectCtrl = null;
-		cmc = null;
-	}
-
 	public static List<CurriculumElementRef> getCurriculumElementRefs(QualityGeneratorConfigs generatorConfigs) {
-		String whiteListConfig = generatorConfigs.getValue(CURRICULUM_ELEMENT_WHITE_LIST);
-		String[] keys = StringHelper.containsNonWhitespace(whiteListConfig)
-				? whiteListConfig.split(KEY_DELIMITER)
-				: new String[0];
-		List<CurriculumElementRef> elementRefs = Arrays.stream(keys)
-				.map(Long::valueOf)
-				.map(CurriculumElementRefImpl::new)
-				.collect(Collectors.toList());
-		return elementRefs;
+		return CurriculumElementListController.getCurriculumElementRefs(generatorConfigs, CONFIG_KEY);
 	}
 	
-	public static void setCurriculumElementRefs(QualityGeneratorConfigs generatorConfigs, List<? extends CurriculumElementRef> elements) {
-		for (CurriculumElementRef element : elements) {
-			doAddCurriculumElement(generatorConfigs, element.getKey().toString());
-		}
-	}
-
-	private static void doAddCurriculumElement(QualityGeneratorConfigs generatorConfigs, String elementKey) {
-		if (StringHelper.containsNonWhitespace(elementKey)) {
-			String whiteListConfig = generatorConfigs.getValue(CURRICULUM_ELEMENT_WHITE_LIST);
-			if (StringHelper.containsNonWhitespace(whiteListConfig)) {
-				String[] keys = whiteListConfig.split(KEY_DELIMITER);
-				if (!Arrays.asList(keys).contains(elementKey)) {
-					whiteListConfig += KEY_DELIMITER + elementKey;
-				}
-			} else {
-				whiteListConfig = elementKey;
-			}
-			generatorConfigs.setValue(CURRICULUM_ELEMENT_WHITE_LIST, whiteListConfig);
-		}
-	}
-
-	private void doSelectCurriculumElement(UserRequest ureq) {
-		List<Organisation> organisations = generatorService.loadGeneratorOrganisations(generator);
-		
-		selectCtrl = new CurriculumElementSelectionController(ureq, getWindowControl(), organisations);
-		listenTo(selectCtrl);
-
-		cmc = new CloseableModalController(getWindowControl(), translate("close"),
-				selectCtrl.getInitialComponent(), true, translate("curriculum.element.select.title"));
-		cmc.activate();
-		listenTo(cmc);
-	}
-
-	private void doAddCurriculumElement(String elementKey) {
-		doAddCurriculumElement(configs, elementKey);
-		loadModel();
-	}
-
-	private void doConfirmRemove(UserRequest ureq, List<CurriculumElement> elements) {
-		if (elements.isEmpty()) {
-			showWarning("curriculum.element.none.selected");
-		} else {
-			removeConfirmationCtrl = new CurriculumElementRemoveConfirmationController(ureq, getWindowControl(), elements);
-			listenTo(removeConfirmationCtrl);
-			
-			cmc = new CloseableModalController(getWindowControl(), translate("close"),
-					removeConfirmationCtrl.getInitialComponent(), true, translate("curriculum.element.remove.confirm.title"));
-			cmc.activate();
-			listenTo(cmc);
-		}
-	}
-
-	private void doRemove(List<CurriculumElement> elements) {
-		List<String> keysToRemove = elements.stream()
-				.map(CurriculumElementRef::getKey)
-				.map(String::valueOf)
-				.collect(Collectors.toList());
-		
-		String whiteListConfig = configs.getValue(CURRICULUM_ELEMENT_WHITE_LIST);
-		String[] splittedKeys = StringHelper.containsNonWhitespace(whiteListConfig)
-				? whiteListConfig.split(KEY_DELIMITER)
-				: new String[0];
-		List<String> currentKeys = Arrays.stream(splittedKeys).collect(Collectors.toList());
-		currentKeys.removeAll(keysToRemove);
-		
-		String keys = currentKeys.stream().collect(joining(KEY_DELIMITER));
-		configs.setValue(CURRICULUM_ELEMENT_WHITE_LIST, keys);
-		loadModel();
-	}
-
-	@Override
-	protected void formOK(UserRequest ureq) {
-		//
-	}
-
-	@Override
-	protected void doDispose() {
-		//
+	public static void setCurriculumElementRefs(QualityGeneratorConfigs generatorConfigs,
+			List<? extends CurriculumElementRef> elements) {
+		CurriculumElementListController.setCurriculumElementRefs(generatorConfigs, elements, CONFIG_KEY);
 	}
 
 }
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/GeneratorController.java b/src/main/java/org/olat/modules/quality/generator/ui/GeneratorController.java
index 15b04d01690..e5ff1659143 100644
--- a/src/main/java/org/olat/modules/quality/generator/ui/GeneratorController.java
+++ b/src/main/java/org/olat/modules/quality/generator/ui/GeneratorController.java
@@ -67,13 +67,15 @@ public class GeneratorController extends BasicController implements TooledContro
 	private Link configurationLink;
 	private Link reportAccessLink;
 	private Link whiteListLink;
+	private Link blackListLink;
 	private final ButtonGroupComponent segmentButtonsCmp;
 	private final TooledStackedPanel stackPanel;
 	private final StackedPanel mainPanel;
 	
 	private GeneratorEditController configCtrl;
 	private GeneratorReportAccessController reportAccessCtrl;
-	private GeneratorWhiteListController whiteListCtrl;
+	private Controller whiteListCtrl;
+	private Controller blackListCtrl;
 	private CloseableModalController cmc;
 	private GeneratorEnableConfirmationController enableConfirmationCtrl;
 	private GeneratorDisableConfirmationController disableConfirmationCtrl;
@@ -104,6 +106,10 @@ public class GeneratorController extends BasicController implements TooledContro
 			whiteListLink = LinkFactory.createLink("generator.white.list", getTranslator(), this);
 			segmentButtonsCmp.addButton(whiteListLink, false);
 		}
+		if (generatorService.hasBlackListController(generator)) {
+			blackListLink = LinkFactory.createLink("generator.black.list", getTranslator(), this);
+			segmentButtonsCmp.addButton(blackListLink, false);
+		}
 		
 		mainPanel = putInitialPanel(new SimpleStackedPanel("dataCollectionSegments"));
 		mainPanel.setContent(new Panel("empty"));
@@ -165,6 +171,8 @@ public class GeneratorController extends BasicController implements TooledContro
 			doOpenReportAccess(ureq);
 		} else if(whiteListLink == source) {
 			doOpenWhiteList(ureq);
+		} else if(blackListLink == source) {
+			doOpenBlackList(ureq);
 		} else if (stackPanel == source && stackPanel.getLastController() == this && event instanceof PopEvent) {
 			PopEvent popEvent = (PopEvent) event;
 			if (popEvent.isClose()) {
@@ -243,6 +251,15 @@ public class GeneratorController extends BasicController implements TooledContro
 		stackPanel.pushController(translate("generator.white.list"), whiteListCtrl);
 		segmentButtonsCmp.setSelectedButton(whiteListLink);
 	}
+	
+	private void doOpenBlackList(UserRequest ureq) {
+		blackListCtrl = generatorService.getBlackListController(ureq, getWindowControl(), secCallback, stackPanel,
+				generator);
+		listenTo(blackListCtrl);
+		stackPanel.popUpToController(this);
+		stackPanel.pushController(translate("generator.black.list"), blackListCtrl);
+		segmentButtonsCmp.setSelectedButton(blackListLink);
+	}
 
 	private void doConfirmEnableGenerator(UserRequest ureq) {
 		if (configCtrl.validateBeforeActivation(ureq)) {
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/GeneratorWhiteListController.java b/src/main/java/org/olat/modules/quality/generator/ui/GeneratorWhiteListController.java
deleted file mode 100644
index 64dca8fbe36..00000000000
--- a/src/main/java/org/olat/modules/quality/generator/ui/GeneratorWhiteListController.java
+++ /dev/null
@@ -1,32 +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.quality.generator.ui;
-
-import org.olat.core.gui.control.Controller;
-
-/**
- * 
- * Initial date: 03.11.2018<br>
- * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
- *
- */
-public interface GeneratorWhiteListController extends Controller {
-
-}
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryBlackListController.java b/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryBlackListController.java
new file mode 100644
index 00000000000..c423f9651c6
--- /dev/null
+++ b/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryBlackListController.java
@@ -0,0 +1,63 @@
+/**
+ * <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.quality.generator.ui;
+
+import java.util.List;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.modules.quality.generator.QualityGeneratorConfigs;
+import org.olat.repository.RepositoryEntryRef;
+
+/**
+ * 
+ * Initial date: 27 May 2020<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class RepositoryEntryBlackListController extends RepositoryEntryListController {
+	
+	private static final String CONFIG_KEY = "course.black.list";
+
+	public RepositoryEntryBlackListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
+			QualityGeneratorConfigs configs) {
+		super(ureq, wControl, stackPanel, configs);
+	}
+
+	@Override
+	protected String getConfigKey() {
+		return CONFIG_KEY;
+	}
+
+	@Override
+	protected String getTablePrefKey() {
+		return "quality-re-black-list";
+	}
+	
+	public static List<RepositoryEntryRef> getRepositoryEntryRefs(QualityGeneratorConfigs generatorConfigs) {
+		return RepositoryEntryListController.getRepositoryEntryRefs(generatorConfigs, CONFIG_KEY);
+	}
+	
+	public static void setRepositoryEntryRefs(QualityGeneratorConfigs generatorConfigs, List<? extends RepositoryEntryRef> entries) {
+		RepositoryEntryListController.setRepositoryEntryRefs(generatorConfigs, entries, CONFIG_KEY);
+	}
+
+}
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryListController.java b/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryListController.java
new file mode 100644
index 00000000000..886d57396f0
--- /dev/null
+++ b/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryListController.java
@@ -0,0 +1,291 @@
+/**
+ * <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.quality.generator.ui;
+
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.form.flexible.FormItem;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
+import org.olat.core.gui.components.form.flexible.elements.FormLink;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormEvent;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
+import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.components.link.LinkFactory;
+import org.olat.core.gui.components.stack.TooledController;
+import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.components.stack.TooledStackedPanel.Align;
+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.gui.control.generic.closablewrapper.CloseableModalController;
+import org.olat.core.util.StringHelper;
+import org.olat.course.CourseModule;
+import org.olat.modules.quality.generator.QualityGeneratorConfigs;
+import org.olat.modules.quality.generator.ui.RepositoryEntryListDataModel.Cols;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryEntryRef;
+import org.olat.repository.RepositoryService;
+import org.olat.repository.controllers.ReferencableEntriesSearchController;
+import org.olat.repository.model.RepositoryEntryRefImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 27 May 2020<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public abstract class RepositoryEntryListController extends FormBasicController implements TooledController {
+
+	private static final String KEY_DELIMITER = ",";
+	
+	private FlexiTableElement tableEl;
+	private RepositoryEntryListDataModel tableModel;
+	private Link addLink;
+	private FormLink removeLink;
+	
+	private CloseableModalController cmc;
+	private ReferencableEntriesSearchController selectCtrl;
+	private RepositoryEntryRemoveConfirmationController removeConfirmationCtrl;
+	
+	private final TooledStackedPanel stackPanel;
+	private final QualityGeneratorConfigs configs;
+	
+	@Autowired
+	private RepositoryService repositoryService;
+
+	public RepositoryEntryListController(UserRequest ureq, WindowControl wControl,
+			TooledStackedPanel stackPanel, QualityGeneratorConfigs configs) {
+		super(ureq, wControl, LAYOUT_BAREBONE);
+		this.stackPanel = stackPanel;
+		this.configs = configs;
+		initForm(ureq);
+	}
+	
+	protected abstract String getConfigKey();
+
+	protected abstract String getTablePrefKey();
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.id));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.displayName));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.identifier));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.begin));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.end));
+
+		tableModel = new RepositoryEntryListDataModel(columnsModel, getLocale());
+		tableEl = uifactory.addTableElement(getWindowControl(), "table", tableModel, 20, true, getTranslator(), formLayout);
+		tableEl.setEmtpyTableMessageKey("repository.entry.empty.table");
+		tableEl.setAndLoadPersistedPreferences(ureq, getTablePrefKey());
+		tableEl.setMultiSelect(true);
+		tableEl.setSelectAllEnable(true);
+		
+		FormLayoutContainer buttons = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
+		formLayout.add("buttons", buttons);
+		buttons.setElementCssClass("o_button_group");
+		removeLink = uifactory.addFormLink("repository.entry.remove", buttons, Link.BUTTON);
+		
+		loadModel();
+	}
+	
+	@Override
+	public void initTools() {
+		addLink = LinkFactory.createToolLink("repository.entry.select", translate("repository.entry.select"), this);
+		addLink.setIconLeftCSS("o_icon o_icon-fw o_icon_qual_gen_re_add");
+		stackPanel.addTool(addLink, Align.right);
+	}
+
+	private void loadModel() {
+		List<Long> entryKeys = getRepositoryEntryRefs(configs, getConfigKey()).stream()
+				.map(RepositoryEntryRef::getKey)
+				.collect(toList());
+		List<RepositoryEntry> entries = repositoryService.loadByKeys(entryKeys);
+		tableModel.setObjects(entries);
+		tableEl.reset(true, true, true);
+		
+		removeLink.setVisible(!entries.isEmpty());
+		flc.setDirty(true);
+	}
+	
+	@Override
+	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
+		if (source == removeLink) {
+			List<RepositoryEntry> entries = getSelectedRepositoryEntries();
+			doConfirmRemove(ureq, entries);
+		}
+	}
+
+	private List<RepositoryEntry> getSelectedRepositoryEntries() {
+		return tableEl.getMultiSelectedIndex().stream()
+				.map(index -> tableModel.getObject(index.intValue()))
+				.collect(Collectors.toList());
+	}
+
+	@Override
+	public void event(UserRequest ureq, Component source, Event event) {
+		if (source == addLink) {
+			doSelectRepositoryEntry(ureq);
+		} 
+		super.event(ureq, source, event);
+	}
+	
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		if (source == selectCtrl) {
+			List<RepositoryEntry> selectedEntries = Collections.emptyList();
+			if (event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRY_SELECTED) {
+				selectedEntries = Collections.singletonList(selectCtrl.getSelectedEntry());
+			} else if (event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRIES_SELECTED) {
+				selectedEntries = selectCtrl.getSelectedEntries();
+			}
+			doAddRepositoryEntries(selectedEntries);
+			cmc.deactivate();
+			cleanUp();
+		} else if (source == removeConfirmationCtrl) {
+			if (Event.DONE_EVENT.equals(event)) {
+				List<RepositoryEntry> entries = removeConfirmationCtrl.getRepositoryEntrys();
+				doRemove(entries);
+			}
+			cmc.deactivate();
+			cleanUp();
+		} else if (source == cmc) {
+			cleanUp();
+		}
+		super.event(ureq, source, event);
+	}
+	
+	private void cleanUp() {
+		removeAsListenerAndDispose(removeConfirmationCtrl);
+		removeAsListenerAndDispose(selectCtrl);
+		removeAsListenerAndDispose(cmc);
+		removeConfirmationCtrl = null;
+		selectCtrl = null;
+		cmc = null;
+	}
+
+	protected static List<RepositoryEntryRef> getRepositoryEntryRefs(QualityGeneratorConfigs generatorConfigs, String configKey) {
+		String whiteListConfig = generatorConfigs.getValue(configKey);
+		String[] keys = StringHelper.containsNonWhitespace(whiteListConfig)
+				? whiteListConfig.split(KEY_DELIMITER)
+				: new String[0];
+		List<RepositoryEntryRef> elementRefs = Arrays.stream(keys)
+				.map(Long::valueOf)
+				.map(RepositoryEntryRefImpl::new)
+				.collect(toList());
+		return elementRefs;
+	}
+	
+	public static void setRepositoryEntryRefs(QualityGeneratorConfigs generatorConfigs,
+			List<? extends RepositoryEntryRef> entries, String configKey) {
+		for (RepositoryEntryRef entry : entries) {
+			doAddRepositoryEntry(generatorConfigs, entry.getKey().toString(), configKey);
+		}
+	}
+
+	private static void doAddRepositoryEntry(QualityGeneratorConfigs generatorConfigs, String entryKey, String configKey) {
+		if (StringHelper.containsNonWhitespace(entryKey)) {
+			String whiteListConfig = generatorConfigs.getValue(configKey);
+			if (StringHelper.containsNonWhitespace(whiteListConfig)) {
+				String[] keys = whiteListConfig.split(KEY_DELIMITER);
+				if (!Arrays.asList(keys).contains(entryKey)) {
+					whiteListConfig += KEY_DELIMITER + entryKey;
+				}
+			} else {
+				whiteListConfig = entryKey;
+			}
+			generatorConfigs.setValue(configKey, whiteListConfig);
+		}
+	}
+	
+	private void doSelectRepositoryEntry(UserRequest ureq) {
+		selectCtrl = new ReferencableEntriesSearchController(getWindowControl(), ureq,
+				new String[] { CourseModule.getCourseTypeName() }, translate("repository.entry.select.title"),
+				false, false, true, false, true);
+		listenTo(selectCtrl);
+
+		cmc = new CloseableModalController(getWindowControl(), translate("close"),
+				selectCtrl.getInitialComponent(), true, translate("repository.entry.select.title"));
+		cmc.activate();
+		listenTo(cmc);
+	}
+
+	private void doAddRepositoryEntries(List<RepositoryEntry> selectedEntries) {
+		setRepositoryEntryRefs(configs, selectedEntries, getConfigKey());
+		loadModel();
+	}
+
+	private void doConfirmRemove(UserRequest ureq, List<RepositoryEntry> entries) {
+		if (entries.isEmpty()) {
+			showWarning("repository.entry.none.selected");
+		} else {
+			removeConfirmationCtrl = new RepositoryEntryRemoveConfirmationController(ureq, getWindowControl(), entries);
+			listenTo(removeConfirmationCtrl);
+			
+			cmc = new CloseableModalController(getWindowControl(), translate("close"),
+					removeConfirmationCtrl.getInitialComponent(), true, translate("repository.entry.remove.confirm.title"));
+			cmc.activate();
+			listenTo(cmc);
+		}
+	}
+
+	private void doRemove(List<RepositoryEntry> entries) {
+		List<String> keysToRemove = entries.stream()
+				.map(RepositoryEntryRef::getKey)
+				.map(String::valueOf)
+				.collect(Collectors.toList());
+		
+		String whiteListConfig = configs.getValue(getConfigKey());
+		String[] splittedKeys = StringHelper.containsNonWhitespace(whiteListConfig)
+				? whiteListConfig.split(KEY_DELIMITER)
+				: new String[0];
+		List<String> currentKeys = Arrays.stream(splittedKeys).collect(Collectors.toList());
+		currentKeys.removeAll(keysToRemove);
+		
+		String keys = currentKeys.stream().collect(joining(KEY_DELIMITER));
+		configs.setValue(getConfigKey(), keys);
+		loadModel();
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+}
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryWhiteListDataModel.java b/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryListDataModel.java
similarity index 92%
rename from src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryWhiteListDataModel.java
rename to src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryListDataModel.java
index ec6a941cae7..f3f9b10117e 100644
--- a/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryWhiteListDataModel.java
+++ b/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryListDataModel.java
@@ -37,12 +37,12 @@ import org.olat.repository.model.RepositoryEntryLifecycle;
  * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
  *
  */
-class RepositoryEntryWhiteListDataModel extends DefaultFlexiTableDataModel<RepositoryEntry>
+class RepositoryEntryListDataModel extends DefaultFlexiTableDataModel<RepositoryEntry>
 		implements SortableFlexiTableDataModel<RepositoryEntry> {
 	
 	private final Locale locale;
 	
-	RepositoryEntryWhiteListDataModel(FlexiTableColumnModel columnsModel, Locale locale) {
+	RepositoryEntryListDataModel(FlexiTableColumnModel columnsModel, Locale locale) {
 		super(columnsModel);
 		this.locale = locale;
 	}
@@ -79,7 +79,7 @@ class RepositoryEntryWhiteListDataModel extends DefaultFlexiTableDataModel<Repos
 
 	@Override
 	public DefaultFlexiTableDataModel<RepositoryEntry> createCopyWithEmptyList() {
-		return new RepositoryEntryWhiteListDataModel(getTableColumnModel(), locale);
+		return new RepositoryEntryListDataModel(getTableColumnModel(), locale);
 	}
 	
 	public enum Cols implements FlexiSortableColumnDef {
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryWhiteListController.java b/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryWhiteListController.java
index 34352c020ec..edb4d32afc8 100644
--- a/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryWhiteListController.java
+++ b/src/main/java/org/olat/modules/quality/generator/ui/RepositoryEntryWhiteListController.java
@@ -19,45 +19,13 @@
  */
 package org.olat.modules.quality.generator.ui;
 
-import static java.util.stream.Collectors.joining;
-import static java.util.stream.Collectors.toList;
-
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
-import java.util.stream.Collectors;
 
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.form.flexible.FormItem;
-import org.olat.core.gui.components.form.flexible.FormItemContainer;
-import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
-import org.olat.core.gui.components.form.flexible.elements.FormLink;
-import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
-import org.olat.core.gui.components.form.flexible.impl.FormEvent;
-import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
-import org.olat.core.gui.components.link.Link;
-import org.olat.core.gui.components.link.LinkFactory;
-import org.olat.core.gui.components.stack.TooledController;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
-import org.olat.core.gui.components.stack.TooledStackedPanel.Align;
-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.gui.control.generic.closablewrapper.CloseableModalController;
-import org.olat.core.util.StringHelper;
-import org.olat.course.CourseModule;
 import org.olat.modules.quality.generator.QualityGeneratorConfigs;
-import org.olat.modules.quality.generator.ui.RepositoryEntryWhiteListDataModel.Cols;
-import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryEntryRef;
-import org.olat.repository.RepositoryService;
-import org.olat.repository.controllers.ReferencableEntriesSearchController;
-import org.olat.repository.model.RepositoryEntryRefImpl;
-import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * 
@@ -65,222 +33,31 @@ import org.springframework.beans.factory.annotation.Autowired;
  * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
  *
  */
-public class RepositoryEntryWhiteListController extends FormBasicController
-		implements GeneratorWhiteListController, TooledController {
+public class RepositoryEntryWhiteListController extends RepositoryEntryListController {
 
-	private static final String COURSE_WHITE_LIST = "course.white.list";
-	private static final String KEY_DELIMITER = ",";
-	
-	private FlexiTableElement tableEl;
-	private RepositoryEntryWhiteListDataModel tableModel;
-	private Link addLink;
-	private FormLink removeLink;
-	
-	private CloseableModalController cmc;
-	private ReferencableEntriesSearchController selectCtrl;
-	private RepositoryEntryRemoveConfirmationController removeConfirmationCtrl;
-	
-	private final TooledStackedPanel stackPanel;
-	private final QualityGeneratorConfigs configs;
-	
-	@Autowired
-	private RepositoryService repositoryService;
+	private static final String CONFIG_KEY = "course.white.list";
 
-	public RepositoryEntryWhiteListController(UserRequest ureq, WindowControl wControl,
-			TooledStackedPanel stackPanel, QualityGeneratorConfigs configs) {
-		super(ureq, wControl, LAYOUT_BAREBONE);
-		this.stackPanel = stackPanel;
-		this.configs = configs;
-		initForm(ureq);
+	public RepositoryEntryWhiteListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
+			QualityGeneratorConfigs configs) {
+		super(ureq, wControl, stackPanel, configs);
 	}
 
 	@Override
-	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
-		FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.id));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.displayName));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.identifier));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.begin));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.end));
-
-		tableModel = new RepositoryEntryWhiteListDataModel(columnsModel, getLocale());
-		tableEl = uifactory.addTableElement(getWindowControl(), "table", tableModel, 20, true, getTranslator(), formLayout);
-		tableEl.setEmtpyTableMessageKey("repository.entry.empty.table");
-		tableEl.setAndLoadPersistedPreferences(ureq, "quality-re-white-list");
-		tableEl.setMultiSelect(true);
-		tableEl.setSelectAllEnable(true);
-		
-		FormLayoutContainer buttons = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
-		formLayout.add("buttons", buttons);
-		buttons.setElementCssClass("o_button_group");
-		removeLink = uifactory.addFormLink("repository.entry.remove", buttons, Link.BUTTON);
-		
-		loadModel();
-	}
-	
-	@Override
-	public void initTools() {
-		addLink = LinkFactory.createToolLink("repository.entry.select", translate("repository.entry.select"), this);
-		addLink.setIconLeftCSS("o_icon o_icon-fw o_icon_qual_gen_re_add");
-		stackPanel.addTool(addLink, Align.right);
+	protected String getConfigKey() {
+		return CONFIG_KEY;
 	}
 
-	private void loadModel() {
-		List<Long> entryKeys = getRepositoryEntryRefs(configs).stream().map(RepositoryEntryRef::getKey).collect(toList());
-		List<RepositoryEntry> entries = repositoryService.loadByKeys(entryKeys);
-		tableModel.setObjects(entries);
-		tableEl.reset(true, true, true);
-		
-		removeLink.setVisible(!entries.isEmpty());
-		flc.setDirty(true);
-	}
-	
-	@Override
-	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
-		if (source == removeLink) {
-			List<RepositoryEntry> entries = getSelectedRepositoryEntries();
-			doConfirmRemove(ureq, entries);
-		}
-	}
-
-	private List<RepositoryEntry> getSelectedRepositoryEntries() {
-		return tableEl.getMultiSelectedIndex().stream()
-				.map(index -> tableModel.getObject(index.intValue()))
-				.collect(Collectors.toList());
-	}
-
-	@Override
-	public void event(UserRequest ureq, Component source, Event event) {
-		if (source == addLink) {
-			doSelectRepositoryEntry(ureq);
-		} 
-		super.event(ureq, source, event);
-	}
-	
 	@Override
-	protected void event(UserRequest ureq, Controller source, Event event) {
-		if (source == selectCtrl) {
-			List<RepositoryEntry> selectedEntries = Collections.emptyList();
-			if (event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRY_SELECTED) {
-				selectedEntries = Collections.singletonList(selectCtrl.getSelectedEntry());
-			} else if (event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRIES_SELECTED) {
-				selectedEntries = selectCtrl.getSelectedEntries();
-			}
-			doAddRepositoryEntries(selectedEntries);
-			cmc.deactivate();
-			cleanUp();
-		} else if (source == removeConfirmationCtrl) {
-			if (Event.DONE_EVENT.equals(event)) {
-				List<RepositoryEntry> entries = removeConfirmationCtrl.getRepositoryEntrys();
-				doRemove(entries);
-			}
-			cmc.deactivate();
-			cleanUp();
-		} else if (source == cmc) {
-			cleanUp();
-		}
-		super.event(ureq, source, event);
+	protected String getTablePrefKey() {
+		return "quality-re-white-list";
 	}
 	
-	private void cleanUp() {
-		removeAsListenerAndDispose(removeConfirmationCtrl);
-		removeAsListenerAndDispose(selectCtrl);
-		removeAsListenerAndDispose(cmc);
-		removeConfirmationCtrl = null;
-		selectCtrl = null;
-		cmc = null;
-	}
-
 	public static List<RepositoryEntryRef> getRepositoryEntryRefs(QualityGeneratorConfigs generatorConfigs) {
-		String whiteListConfig = generatorConfigs.getValue(COURSE_WHITE_LIST);
-		String[] keys = StringHelper.containsNonWhitespace(whiteListConfig)
-				? whiteListConfig.split(KEY_DELIMITER)
-				: new String[0];
-		List<RepositoryEntryRef> elementRefs = Arrays.stream(keys)
-				.map(Long::valueOf)
-				.map(RepositoryEntryRefImpl::new)
-				.collect(toList());
-		return elementRefs;
+		return RepositoryEntryListController.getRepositoryEntryRefs(generatorConfigs, CONFIG_KEY);
 	}
 	
 	public static void setRepositoryEntryRefs(QualityGeneratorConfigs generatorConfigs, List<? extends RepositoryEntryRef> entries) {
-		for (RepositoryEntryRef entry : entries) {
-			doAddRepositoryEntry(generatorConfigs, entry.getKey().toString());
-		}
-	}
-
-	private static void doAddRepositoryEntry(QualityGeneratorConfigs generatorConfigs, String entryKey) {
-		if (StringHelper.containsNonWhitespace(entryKey)) {
-			String whiteListConfig = generatorConfigs.getValue(COURSE_WHITE_LIST);
-			if (StringHelper.containsNonWhitespace(whiteListConfig)) {
-				String[] keys = whiteListConfig.split(KEY_DELIMITER);
-				if (!Arrays.asList(keys).contains(entryKey)) {
-					whiteListConfig += KEY_DELIMITER + entryKey;
-				}
-			} else {
-				whiteListConfig = entryKey;
-			}
-			generatorConfigs.setValue(COURSE_WHITE_LIST, whiteListConfig);
-		}
-	}
-	
-	private void doSelectRepositoryEntry(UserRequest ureq) {
-		selectCtrl = new ReferencableEntriesSearchController(getWindowControl(), ureq,
-				new String[] { CourseModule.getCourseTypeName() }, translate("repository.entry.select.title"),
-				false, false, true, false, true);
-		listenTo(selectCtrl);
-
-		cmc = new CloseableModalController(getWindowControl(), translate("close"),
-				selectCtrl.getInitialComponent(), true, translate("repository.entry.select.title"));
-		cmc.activate();
-		listenTo(cmc);
-	}
-
-	private void doAddRepositoryEntries(List<RepositoryEntry> selectedEntries) {
-		setRepositoryEntryRefs(configs, selectedEntries);
-		loadModel();
-	}
-
-	private void doConfirmRemove(UserRequest ureq, List<RepositoryEntry> entries) {
-		if (entries.isEmpty()) {
-			showWarning("repository.entry.none.selected");
-		} else {
-			removeConfirmationCtrl = new RepositoryEntryRemoveConfirmationController(ureq, getWindowControl(), entries);
-			listenTo(removeConfirmationCtrl);
-			
-			cmc = new CloseableModalController(getWindowControl(), translate("close"),
-					removeConfirmationCtrl.getInitialComponent(), true, translate("repository.entry.remove.confirm.title"));
-			cmc.activate();
-			listenTo(cmc);
-		}
-	}
-
-	private void doRemove(List<RepositoryEntry> entries) {
-		List<String> keysToRemove = entries.stream()
-				.map(RepositoryEntryRef::getKey)
-				.map(String::valueOf)
-				.collect(Collectors.toList());
-		
-		String whiteListConfig = configs.getValue(COURSE_WHITE_LIST);
-		String[] splittedKeys = StringHelper.containsNonWhitespace(whiteListConfig)
-				? whiteListConfig.split(KEY_DELIMITER)
-				: new String[0];
-		List<String> currentKeys = Arrays.stream(splittedKeys).collect(Collectors.toList());
-		currentKeys.removeAll(keysToRemove);
-		
-		String keys = currentKeys.stream().collect(joining(KEY_DELIMITER));
-		configs.setValue(COURSE_WHITE_LIST, keys);
-		loadModel();
-	}
-
-	@Override
-	protected void formOK(UserRequest ureq) {
-		//
-	}
-
-	@Override
-	protected void doDispose() {
-		//
+		RepositoryEntryListController.setRepositoryEntryRefs(generatorConfigs, entries, CONFIG_KEY);
 	}
 
 }
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/quality/generator/ui/_i18n/LocalStrings_de.properties
index 42e2b335382..bb51f1efc89 100644
--- a/src/main/java/org/olat/modules/quality/generator/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/quality/generator/ui/_i18n/LocalStrings_de.properties
@@ -16,6 +16,7 @@ curriculum.element.select.curriculum.element=Curriculumelement
 curriculum.element.select.title=Curriculumelement hinzuf\u00FCgen
 curriculum.element.type.name=Typ
 details.delete.error.quality.form.entry=Lernressource "{0}" kann nicht gel\u00F6scht werden. Es wird von einen Datenerhebungen ben\u00FCtzt.
+generator.black.list=Negativliste
 generator.configuration=Konfiguration
 generator.create=Generator erstellen
 generator.create.button=Erstellen
diff --git a/src/main/java/org/olat/modules/quality/generator/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/quality/generator/ui/_i18n/LocalStrings_en.properties
index 9ab67696d14..5ce7f846be2 100644
--- a/src/main/java/org/olat/modules/quality/generator/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/quality/generator/ui/_i18n/LocalStrings_en.properties
@@ -17,6 +17,7 @@ curriculum.element.select.curriculum=Curriculum
 curriculum.element.select.title=Add curriculum element
 curriculum.element.type.name=Type
 details.delete.error.quality.form.entry=This learning resource "{0}" cannot be deleted. There are data collections which need it.
+generator.black.list=Black list
 generator.configuration=Configuration
 generator.create.button=Create
 generator.create.create=Create
diff --git a/src/test/java/org/olat/modules/quality/generator/provider/course/manager/CourseProviderDAOTest.java b/src/test/java/org/olat/modules/quality/generator/provider/course/manager/CourseProviderDAOTest.java
index 5fbfc2fd7a9..b5bce19395d 100644
--- a/src/test/java/org/olat/modules/quality/generator/provider/course/manager/CourseProviderDAOTest.java
+++ b/src/test/java/org/olat/modules/quality/generator/provider/course/manager/CourseProviderDAOTest.java
@@ -239,14 +239,14 @@ public class CourseProviderDAOTest extends OlatTestCase {
 	}
 	
 	@Test
-	public void shouldFilterByRepositoryEntryRefs() {
+	public void shouldFilterByWhiteListRefs() {
 		RepositoryEntry course1 = createEntry(null, null, null);
 		RepositoryEntry course2 = createEntry(null, null, null);
 		RepositoryEntry other = createEntry(null, null, null);
 		dbInstance.commitAndCloseSession();
 		
 		SearchParameters seachParameters = new SearchParameters();
-		seachParameters.setRepositoryEntryRefs(asList(course1, course2));
+		seachParameters.setWhiteListRefs(asList(course1, course2));
 		List<RepositoryEntry> courses = sut.loadCourses(seachParameters);
 
 		assertThat(courses)
@@ -254,6 +254,22 @@ public class CourseProviderDAOTest extends OlatTestCase {
 				.doesNotContain(other);
 	}
 	
+	@Test
+	public void shouldFilterByBlackListRefs() {
+		RepositoryEntry course1 = createEntry(null, null, null);
+		RepositoryEntry course2 = createEntry(null, null, null);
+		RepositoryEntry blackList = createEntry(null, null, null);
+		dbInstance.commitAndCloseSession();
+		
+		SearchParameters seachParameters = new SearchParameters();
+		seachParameters.setBlackListRefs(asList(blackList));
+		List<RepositoryEntry> courses = sut.loadCourses(seachParameters);
+
+		assertThat(courses)
+				.contains(course1, course2)
+				.doesNotContain(blackList);
+	}
+	
 	@Test
 	public void shouldFilterByGeneratorDataCollectionStart() {
 		Date start = nextHour();
diff --git a/src/test/java/org/olat/modules/quality/generator/provider/courselectures/manager/CourseLecturesProviderDAOTest.java b/src/test/java/org/olat/modules/quality/generator/provider/courselectures/manager/CourseLecturesProviderDAOTest.java
index 44da5408c1e..aa30cacb39c 100644
--- a/src/test/java/org/olat/modules/quality/generator/provider/courselectures/manager/CourseLecturesProviderDAOTest.java
+++ b/src/test/java/org/olat/modules/quality/generator/provider/courselectures/manager/CourseLecturesProviderDAOTest.java
@@ -203,7 +203,7 @@ public class CourseLecturesProviderDAOTest extends OlatTestCase {
 	}
 	
 	@Test
-	public void shouldFilterLectureBlockInfosByCurriculumElements() {
+	public void shouldFilterLectureBlockInfosByWhiteList() {
 		Identity teacher = JunitTestHelper.createAndPersistIdentityAsRndUser("");
 		Organisation organisation = organisationService.createOrganisation("org", "Org", null, null, null);
 		Curriculum curriculum = curriculumService.createCurriculum("Curriculum", "Curriculum", null, organisation);
@@ -226,14 +226,45 @@ public class CourseLecturesProviderDAOTest extends OlatTestCase {
 
 		SearchParameters searchParams = new SearchParameters();
 		searchParams.setTeacherRef(teacher);
-		searchParams.setCurriculumElementRefs(Arrays.asList(element));
+		searchParams.setWhiteListRefs(Arrays.asList(element));
 		List<LectureBlockInfo> infos = sut.loadLectureBlockInfo(searchParams);
 
 		assertThat(infos).extracting(LectureBlockInfo::getCourseRepoKey)
 				.containsExactlyInAnyOrder(course1.getKey(), course2.getKey())
 				.doesNotContain(otherCourse.getKey());
 	}
+	
+	@Test
+	public void shouldFilterLectureBlockInfosByBlackList() {
+		Identity teacher = JunitTestHelper.createAndPersistIdentityAsRndUser("");
+		Organisation organisation = organisationService.createOrganisation("org", "Org", null, null, null);
+		Curriculum curriculum = curriculumService.createCurriculum("Curriculum", "Curriculum", null, organisation);
+		CurriculumElement element = curriculumService.createCurriculumElement("Element", "Element",
+				CurriculumElementStatus.active, null, null, null, null, CurriculumCalendars.disabled,
+				CurriculumLectures.disabled, CurriculumLearningProgress.disabled, curriculum);
+		CurriculumElement otherElement = curriculumService.createCurriculumElement("Element", "Element",
+				CurriculumElementStatus.active, null, null, null, null, CurriculumCalendars.disabled,
+				CurriculumLectures.disabled, CurriculumLearningProgress.disabled, curriculum);
+		RepositoryEntry course1 = JunitTestHelper.createAndPersistRepositoryEntry();
+		RepositoryEntry course2 = JunitTestHelper.createAndPersistRepositoryEntry();
+		RepositoryEntry otherCourse = JunitTestHelper.createAndPersistRepositoryEntry();
+		createLectureBlock(course1, teacher, 1);
+		createLectureBlock(course2, teacher, 1);
+		createLectureBlock(otherCourse, teacher, 1);
+		curriculumService.addRepositoryEntry(element, course1, false);
+		curriculumService.addRepositoryEntry(element, course2, false);
+		curriculumService.addRepositoryEntry(otherElement, otherCourse, false);
+		dbInstance.commitAndCloseSession();
+
+		SearchParameters searchParams = new SearchParameters();
+		searchParams.setTeacherRef(teacher);
+		searchParams.setBlackListRefs(Arrays.asList(otherElement));
+		List<LectureBlockInfo> infos = sut.loadLectureBlockInfo(searchParams);
 
+		assertThat(infos).extracting(LectureBlockInfo::getCourseRepoKey)
+				.containsExactlyInAnyOrder(course1.getKey(), course2.getKey())
+				.doesNotContain(otherCourse.getKey());
+	}
 	
 	@Test
 	public void shouldFilterLectureBlockInfosByEndDate() {
@@ -581,11 +612,11 @@ public class CourseLecturesProviderDAOTest extends OlatTestCase {
 		assertThat(count).isEqualTo(2);
 	}
 	
-	public LectureBlock createLectureBlock(RepositoryEntry course, Identity teacher, int numLectures) {
+	private LectureBlock createLectureBlock(RepositoryEntry course, Identity teacher, int numLectures) {
 		return createLectureBlock(course, teacher, numLectures, nextHour(), nextHour());
 	}
 	
-	public LectureBlock createLectureBlock(RepositoryEntry course, Identity teacher, int numLectures, Date start, Date end) {
+	private LectureBlock createLectureBlock(RepositoryEntry course, Identity teacher, int numLectures, Date start, Date end) {
 		LectureBlock lectureBlock = lectureService.createLectureBlock(course);
 		lectureBlock.setStartDate(start);
 		lectureBlock.setEndDate(end);
diff --git a/src/test/java/org/olat/modules/quality/generator/provider/curriculumelement/CurriculumElementProviderTest.java b/src/test/java/org/olat/modules/quality/generator/provider/curriculumelement/CurriculumElementProviderTest.java
index c77d260e585..26a6124251a 100644
--- a/src/test/java/org/olat/modules/quality/generator/provider/curriculumelement/CurriculumElementProviderTest.java
+++ b/src/test/java/org/olat/modules/quality/generator/provider/curriculumelement/CurriculumElementProviderTest.java
@@ -84,7 +84,7 @@ public class CurriculumElementProviderTest extends OlatTestCase {
 		
 		QualityGenerator generator = createGeneratorInDefaultOrganisation();
 		String durationDays = "10";
-		QualityGeneratorConfigs configs = createAfterSecondLectureConfigs(generator, durationDays, curriculumElement);
+		QualityGeneratorConfigs configs = createZeroDaysAfterBeginConfigs(generator, durationDays, curriculumElement);
 		
 		Date lastRun = new GregorianCalendar(2010, 6, 1).getTime();
 		Date now = new GregorianCalendar(2010, 6, 13).getTime();
@@ -99,7 +99,7 @@ public class CurriculumElementProviderTest extends OlatTestCase {
 				.doesNotContain(defaultOrganisation);
 	}
 
-	private QualityGeneratorConfigs createAfterSecondLectureConfigs(QualityGenerator generator, String durationDays,
+	private QualityGeneratorConfigs createZeroDaysAfterBeginConfigs(QualityGenerator generator, String durationDays,
 			CurriculumElementRef curriculumElementRef) {
 		QualityGeneratorConfigs configs = new QualityGeneratorConfigsImpl(generator);
 		configs.setValue(CurriculumElementProvider.CONFIG_KEY_TITLE, "DATA_COLLECTION_TITLE");
diff --git a/src/test/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/CurriculumElementProviderDAOTest.java b/src/test/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/CurriculumElementProviderDAOTest.java
index a85a1aab578..ea7a965c0df 100644
--- a/src/test/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/CurriculumElementProviderDAOTest.java
+++ b/src/test/java/org/olat/modules/quality/generator/provider/curriculumelement/manager/CurriculumElementProviderDAOTest.java
@@ -175,7 +175,7 @@ public class CurriculumElementProviderDAOTest extends OlatTestCase {
 	}
 	
 	@Test
-	public void shouldFilterByCurriculumElement() {
+	public void shouldFilterByWhiteList() {
 		Organisation organisation = organisationService.createOrganisation(random(), random(), null, null, null);
 		Curriculum curriculum = curriculumService.createCurriculum(random(), random(), null, organisation);
 		dbInstance.commitAndCloseSession();
@@ -192,7 +192,35 @@ public class CurriculumElementProviderDAOTest extends OlatTestCase {
 		dbInstance.commitAndCloseSession();
 
 		SearchParameters searchParams = new SearchParameters();
-		searchParams.setCurriculumElementRefs(asList(curriculumElement1, curriculumElement2));
+		searchParams.setWhiteListRefs(asList(curriculumElement1, curriculumElement2));
+		List<CurriculumElement> pending = sut.loadPending(searchParams);
+
+		assertThat(pending)
+				.containsExactlyInAnyOrder(curriculumElement1, curriculumElement2)
+				.doesNotContain(otherCurriculumElement);
+	}
+	
+	@Test
+	public void shouldFilterByBlackList() {
+		Organisation organisation = organisationService.createOrganisation(random(), random(), null, null, null);
+		Curriculum curriculum = curriculumService.createCurriculum(random(), random(), null, organisation);
+		CurriculumElementType type = curriculumService.createCurriculumElementType(random(), random(), random(), null);
+		dbInstance.commitAndCloseSession();
+
+		CurriculumElement curriculumElement1 = curriculumService.createCurriculumElement(random(), random(),
+				CurriculumElementStatus.active, oneDayAgo(), inOneDay(), null, type, CurriculumCalendars.disabled,
+				CurriculumLectures.disabled, CurriculumLearningProgress.disabled, curriculum);
+		CurriculumElement curriculumElement2 = curriculumService.createCurriculumElement(random(), random(),
+				CurriculumElementStatus.active, oneDayAgo(), inOneDay(), null, type, CurriculumCalendars.disabled,
+				CurriculumLectures.disabled, CurriculumLearningProgress.disabled, curriculum);
+		CurriculumElement otherCurriculumElement = curriculumService.createCurriculumElement(random(), random(),
+				CurriculumElementStatus.active, oneDayAgo(), inOneDay(), null, type, CurriculumCalendars.disabled,
+				CurriculumLectures.disabled, CurriculumLearningProgress.disabled, curriculum);
+		dbInstance.commitAndCloseSession();
+
+		SearchParameters searchParams = new SearchParameters();
+		searchParams.setCeTypeKey(type.getKey());
+		searchParams.setBlackListRefs(asList(otherCurriculumElement));
 		List<CurriculumElement> pending = sut.loadPending(searchParams);
 
 		assertThat(pending)
-- 
GitLab