From 7d76b5bbbc608b10c74ea8af23aa49c01f043ec1 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Thu, 26 Nov 2015 16:15:18 +0100
Subject: [PATCH] OO-1593: add batch actions, bulk assessment, certificate
 generation, efficiency statement renew...

---
 .../assessment/AssessmentMainController.java  | 11 ++-
 ...ficiencyStatementAssessmentController.java | 16 ++--
 .../BulkAssessmentOverviewController.java     | 37 +++++----
 ...BulkAssessment_1_SelectCourseNodeStep.java | 11 ++-
 .../bulk/SelectCourseNodeStepForm.java        | 11 ++-
 .../AssessmentCourseOverviewController.java   | 43 ++++++++++-
 .../AssessmentIdentitiesCourseController.java | 75 ++++++++++++++++++-
 ...essmentIdentitiesCourseNodeController.java | 59 ++++++++++++++-
 .../ui/tool/AssessmentToolController.java     | 44 ++++++++++-
 .../ui/tool/_content/course_overview.html     | 31 +++++++-
 .../ui/tool/_content/identity_course.html     |  8 +-
 .../tool/_content/identity_courseelement.html | 10 ++-
 .../ui/tool/_i18n/LocalStrings_de.properties  |  2 +
 .../ui/tool/_i18n/LocalStrings_en.properties  |  2 +
 .../ui/CertificatesSelectionController.java   | 16 ++--
 .../ui/CertificatesWizardController.java      | 14 ++--
 .../ui/Certificates_1_SelectionStep.java      | 10 +--
 17 files changed, 321 insertions(+), 79 deletions(-)

diff --git a/src/main/java/org/olat/course/assessment/AssessmentMainController.java b/src/main/java/org/olat/course/assessment/AssessmentMainController.java
index 423954c78cf..a2467f37694 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentMainController.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentMainController.java
@@ -962,7 +962,8 @@ public class AssessmentMainController extends MainLayoutBasicController implemen
 		if(courseConfig.isManualCertificationEnabled()) {
 			if(courseNode == null || courseNode == course.getRunStructure().getRootNode()) {
 				removeAsListenerAndDispose(certificateWizardCtrl);
-				certificateWizardCtrl = new CertificatesWizardController(ureq, getWindowControl(), tdm, ores, hasAssessableNodes);
+				RepositoryEntry courseEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
+				certificateWizardCtrl = new CertificatesWizardController(ureq, getWindowControl(), tdm, courseEntry, hasAssessableNodes);
 				listenTo(certificateWizardCtrl);
 
 				String toolCmpName = "ctrl_" + (count++);
@@ -1083,14 +1084,18 @@ public class AssessmentMainController extends MainLayoutBasicController implemen
 	}
 	
 	private void doBulkAssessment(UserRequest ureq) {
-		bulkAssOverviewCtrl = new BulkAssessmentOverviewController(ureq, getWindowControl(), ores);
+		ICourse course = CourseFactory.loadCourse(ores);
+		RepositoryEntry courseEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
+		bulkAssOverviewCtrl = new BulkAssessmentOverviewController(ureq, getWindowControl(), courseEntry);
 		listenTo(bulkAssOverviewCtrl);
 		main.setContent(bulkAssOverviewCtrl.getInitialComponent());
 	}
 	
 	private void doEfficiencyStatement(UserRequest ureq) {
 		removeAsListenerAndDispose(esac);
-		esac = new EfficiencyStatementAssessmentController(ureq, getWindowControl(), ores);
+		ICourse course = CourseFactory.loadCourse(ores);
+		RepositoryEntry courseEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
+		esac = new EfficiencyStatementAssessmentController(ureq, getWindowControl(), courseEntry);
 		listenTo(esac);
 		main.setContent(esac.getInitialComponent());
 	}
diff --git a/src/main/java/org/olat/course/assessment/EfficiencyStatementAssessmentController.java b/src/main/java/org/olat/course/assessment/EfficiencyStatementAssessmentController.java
index 76c597e2082..cf18b3cb740 100644
--- a/src/main/java/org/olat/course/assessment/EfficiencyStatementAssessmentController.java
+++ b/src/main/java/org/olat/course/assessment/EfficiencyStatementAssessmentController.java
@@ -20,7 +20,6 @@
 package org.olat.course.assessment;
 
 import org.olat.NewControllerFactory;
-import org.olat.core.CoreSpringFactory;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItem;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
@@ -39,11 +38,11 @@ import org.olat.core.id.context.BusinessControl;
 import org.olat.core.id.context.BusinessControlFactory;
 import org.olat.core.util.coordinate.CoordinatorManager;
 import org.olat.core.util.event.GenericEventListener;
+import org.olat.core.util.resource.OresHelper;
 import org.olat.course.CourseFactory;
 import org.olat.course.CourseModule;
 import org.olat.course.ICourse;
 import org.olat.repository.RepositoryEntry;
-import org.olat.repository.RepositoryManager;
 
 /**
  * 
@@ -56,12 +55,12 @@ public class EfficiencyStatementAssessmentController extends FormBasicController
 	private DialogBoxController recalculateEfficiencyDC;
 	
 	private final OLATResourceable ores;
-	private final RepositoryManager repositoryManager;
+	private final RepositoryEntry courseEntry;
 	
-	public EfficiencyStatementAssessmentController(UserRequest ureq, WindowControl wControl, OLATResourceable courseOres) {
+	public EfficiencyStatementAssessmentController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry) {
 		super(ureq, wControl, "assessment_eff_statement");
-		this.ores = courseOres;
-		repositoryManager = CoreSpringFactory.getImpl(RepositoryManager.class);
+		this.courseEntry = courseEntry;
+		this.ores = OresHelper.clone(courseEntry.getOlatResource());
 		CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, getIdentity(), ores);
 		initForm(ureq);
 	}
@@ -70,7 +69,7 @@ public class EfficiencyStatementAssessmentController extends FormBasicController
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 		if(formLayout instanceof FormLayoutContainer) {
 			FormLayoutContainer container = (FormLayoutContainer)formLayout;
-			ICourse course = CourseFactory.loadCourse(ores);
+			ICourse course = CourseFactory.loadCourse(courseEntry);
 		
 			boolean enabled = course.getCourseEnvironment().getCourseConfig().isEfficencyStatementEnabled();
 			String enableStr = this.translate(enabled ? "efficiencystatement.config.on" : "efficiencystatement.config.off");
@@ -125,8 +124,7 @@ public class EfficiencyStatementAssessmentController extends FormBasicController
 	}
 
 	private void openConfiguration(UserRequest ureq) {
-		RepositoryEntry re = repositoryManager.lookupRepositoryEntry(ores, false);
-		String resourceUrl = "[RepositoryEntry:" + re.getKey() + "][CertificationSettings:0]";
+		String resourceUrl = "[RepositoryEntry:" + courseEntry.getKey() + "][CertificationSettings:0]";
 		BusinessControl bc = BusinessControlFactory.getInstance().createFromString(resourceUrl);
 		WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl());
 		NewControllerFactory.getInstance().launch(ureq, bwControl);
diff --git a/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentOverviewController.java b/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentOverviewController.java
index bec6d4a1650..b02d643472f 100644
--- a/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentOverviewController.java
+++ b/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentOverviewController.java
@@ -25,7 +25,6 @@ import java.util.Date;
 import java.util.List;
 
 import org.olat.NewControllerFactory;
-import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.services.taskexecutor.Task;
 import org.olat.core.commons.services.taskexecutor.TaskExecutorManager;
 import org.olat.core.commons.services.taskexecutor.ui.TaskStatusRenderer;
@@ -54,7 +53,6 @@ import org.olat.core.gui.control.generic.wizard.StepRunnerCallback;
 import org.olat.core.gui.control.generic.wizard.StepsMainRunController;
 import org.olat.core.gui.control.generic.wizard.StepsRunContext;
 import org.olat.core.id.Identity;
-import org.olat.core.id.OLATResourceable;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
 import org.olat.course.Structure;
@@ -64,9 +62,9 @@ import org.olat.course.assessment.model.BulkAssessmentDatas;
 import org.olat.course.assessment.model.BulkAssessmentFeedback;
 import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.CourseNode;
-import org.olat.repository.RepositoryManager;
-import org.olat.resource.OLATResource;
+import org.olat.repository.RepositoryEntry;
 import org.olat.user.UserManager;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * 
@@ -83,19 +81,18 @@ public class BulkAssessmentOverviewController extends FormBasicController {
 	private StepsMainRunController bulkAssessmentCtrl;
 	private DialogBoxController errorCtrl;
 	
-	private final OLATResourceable courseOres;
-	private final OLATResource courseRe;
-	private final UserManager userManager;
-	private final TaskExecutorManager taskManager;
+	private final RepositoryEntry courseEntry;
+	
+	@Autowired
+	private UserManager userManager;
+	@Autowired
+	private TaskExecutorManager taskManager;
 	
 	private Task editedTask;
 	
-	public BulkAssessmentOverviewController(UserRequest ureq, WindowControl wControl, OLATResourceable courseOres) {
+	public BulkAssessmentOverviewController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry) {
 		super(ureq, wControl, "overview");
-		this.courseOres = courseOres;
-		courseRe = RepositoryManager.getInstance().lookupRepositoryEntry(courseOres, false).getOlatResource();	
-		userManager = CoreSpringFactory.getImpl(UserManager.class);
-		taskManager = CoreSpringFactory.getImpl(TaskExecutorManager.class);
+		this.courseEntry = courseEntry;
 		
 		initForm(ureq);
 	}
@@ -130,9 +127,9 @@ public class BulkAssessmentOverviewController extends FormBasicController {
 	}
 	
 	private void reloadTaskModel() {
-		List<Task> tasks = taskManager.getTasks(courseRe);
+		List<Task> tasks = taskManager.getTasks(courseEntry.getOlatResource());
 		List<TaskData> taskDatas = new ArrayList<TaskData>(tasks.size());
-		ICourse course = CourseFactory.loadCourse(courseOres);
+		ICourse course = CourseFactory.loadCourse(courseEntry);
 		Structure structure = course.getRunStructure();
 		
 		for(Task task:tasks) {
@@ -251,12 +248,12 @@ public class BulkAssessmentOverviewController extends FormBasicController {
 		removeAsListenerAndDispose(bulkAssessmentCtrl);
 		
 		List<AssessableCourseNode> nodes = new ArrayList<>();
-		ICourse course = CourseFactory.loadCourse(courseOres);
+		ICourse course = CourseFactory.loadCourse(courseEntry);
 		collectBulkAssessableCourseNode(course.getRunStructure().getRootNode(), nodes);
 
 		Step start;
 		if(nodes.size() > 1) {
-			start = new BulkAssessment_1_SelectCourseNodeStep(ureq, courseOres);
+			start = new BulkAssessment_1_SelectCourseNodeStep(ureq, courseEntry);
 		} else if(nodes.size() == 1){
 			start = new BulkAssessment_2_DatasStep(ureq, nodes.get(0));
 		} else {
@@ -339,7 +336,7 @@ public class BulkAssessmentOverviewController extends FormBasicController {
 	}
 	
 	private Feedback doUpdateBulkAssessment(Task task, AssessableCourseNode node, Date scheduledDate, BulkAssessmentDatas datas) {
-		BulkAssessmentTask runnable = new BulkAssessmentTask(courseOres, node, datas, getIdentity().getKey());
+		BulkAssessmentTask runnable = new BulkAssessmentTask(courseEntry.getOlatResource(), node, datas, getIdentity().getKey());
 		Feedback feedback;
 		if(scheduledDate == null) {
 			List<BulkAssessmentFeedback> feedbacks = runnable.process();
@@ -355,13 +352,13 @@ public class BulkAssessmentOverviewController extends FormBasicController {
 	}
 	
 	private Feedback doBulkAssessment(AssessableCourseNode node, Date scheduledDate, BulkAssessmentDatas datas) {
-		BulkAssessmentTask task = new BulkAssessmentTask(courseOres, node, datas, getIdentity().getKey());
+		BulkAssessmentTask task = new BulkAssessmentTask(courseEntry.getOlatResource(), node, datas, getIdentity().getKey());
 		Feedback feedback;
 		if(scheduledDate == null) {
 			List<BulkAssessmentFeedback> feedbacks = task.process();
 			feedback = new Feedback(true, feedbacks);
 		} else {
-			taskManager.execute(task, getIdentity(), courseRe, node.getIdent(), scheduledDate);
+			taskManager.execute(task, getIdentity(), courseEntry.getOlatResource(), node.getIdent(), scheduledDate);
 			feedback = new Feedback(false, null);
 		}
 		return feedback;
diff --git a/src/main/java/org/olat/course/assessment/bulk/BulkAssessment_1_SelectCourseNodeStep.java b/src/main/java/org/olat/course/assessment/bulk/BulkAssessment_1_SelectCourseNodeStep.java
index 623f83e0687..2ffb534bd9d 100644
--- a/src/main/java/org/olat/course/assessment/bulk/BulkAssessment_1_SelectCourseNodeStep.java
+++ b/src/main/java/org/olat/course/assessment/bulk/BulkAssessment_1_SelectCourseNodeStep.java
@@ -26,7 +26,7 @@ import org.olat.core.gui.control.generic.wizard.BasicStep;
 import org.olat.core.gui.control.generic.wizard.PrevNextFinishConfig;
 import org.olat.core.gui.control.generic.wizard.StepFormController;
 import org.olat.core.gui.control.generic.wizard.StepsRunContext;
-import org.olat.core.id.OLATResourceable;
+import org.olat.repository.RepositoryEntry;
 
 /**
  * 
@@ -36,11 +36,11 @@ import org.olat.core.id.OLATResourceable;
  */
 public class BulkAssessment_1_SelectCourseNodeStep extends BasicStep {
 	
-	private final OLATResourceable courseOres;
+	private final RepositoryEntry courseEntry;
 	
-	public BulkAssessment_1_SelectCourseNodeStep(UserRequest ureq, OLATResourceable courseOres) {
+	public BulkAssessment_1_SelectCourseNodeStep(UserRequest ureq, RepositoryEntry courseEntry) {
 		super(ureq);
-		this.courseOres = courseOres;
+		this.courseEntry = courseEntry;
 		setI18nTitleAndDescr("choose.node.title", "choose.node.title");
 		setNextStep(new BulkAssessment_2_DatasStep(ureq));
 	}
@@ -53,7 +53,6 @@ public class BulkAssessment_1_SelectCourseNodeStep extends BasicStep {
 	@Override
 	public StepFormController getStepController(UserRequest ureq, WindowControl wControl, StepsRunContext context, Form form) {
 		form.setMultipartEnabled(true);
-		SelectCourseNodeStepForm ctrl = new SelectCourseNodeStepForm(ureq, wControl, courseOres, context, form);
-		return ctrl;
+		return new SelectCourseNodeStepForm(ureq, wControl, courseEntry, context, form);
 	}
 }
diff --git a/src/main/java/org/olat/course/assessment/bulk/SelectCourseNodeStepForm.java b/src/main/java/org/olat/course/assessment/bulk/SelectCourseNodeStepForm.java
index 55b69fbdd75..658b9ec50a7 100644
--- a/src/main/java/org/olat/course/assessment/bulk/SelectCourseNodeStepForm.java
+++ b/src/main/java/org/olat/course/assessment/bulk/SelectCourseNodeStepForm.java
@@ -42,11 +42,11 @@ import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.generic.wizard.StepFormBasicController;
 import org.olat.core.gui.control.generic.wizard.StepsEvent;
 import org.olat.core.gui.control.generic.wizard.StepsRunContext;
-import org.olat.core.id.OLATResourceable;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
 import org.olat.course.assessment.manager.BulkAssessmentTask;
 import org.olat.course.nodes.CourseNode;
+import org.olat.repository.RepositoryEntry;
 
 /**
  * 
@@ -56,22 +56,21 @@ import org.olat.course.nodes.CourseNode;
  */
 public class SelectCourseNodeStepForm extends StepFormBasicController {
 	
-	private final OLATResourceable courseOres;
+	private final RepositoryEntry courseEntry;
 	
 	private FlexiTableElement tableEl;
 	private NodeTableDataModel tableModel;
 	
 	public SelectCourseNodeStepForm(UserRequest ureq, WindowControl wControl,
-			OLATResourceable courseOres, StepsRunContext runContext, Form rootForm) {
+			RepositoryEntry courseEntry, StepsRunContext runContext, Form rootForm) {
 		super(ureq, wControl, rootForm, runContext, LAYOUT_CUSTOM, "select_node");
-		this.courseOres = courseOres;
-		
+		this.courseEntry = courseEntry;
 		initForm(ureq);
 	}
 
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
-		ICourse course = CourseFactory.loadCourse(courseOres);
+		ICourse course = CourseFactory.loadCourse(courseEntry);
 		CourseNode rootNode = course.getRunStructure().getRootNode();
 		List<Node> courseNodes = addManualTaskNodesAndParentsToList(0, rootNode);
 		tableModel = new NodeTableDataModel(courseNodes);
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseOverviewController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseOverviewController.java
index 5f46e9faa87..0ec7d7c98b5 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseOverviewController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseOverviewController.java
@@ -21,17 +21,27 @@ package org.olat.course.assessment.ui.tool;
 
 import java.util.List;
 
+import org.olat.core.commons.services.notifications.PublisherData;
+import org.olat.core.commons.services.notifications.SubscriptionContext;
+import org.olat.core.commons.services.notifications.ui.ContextualSubscriptionController;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.link.Link;
 import org.olat.core.gui.components.link.LinkFactory;
 import org.olat.core.gui.components.velocity.VelocityContainer;
+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.controller.BasicController;
 import org.olat.core.gui.control.generic.dtabs.Activateable2;
 import org.olat.core.id.context.ContextEntry;
 import org.olat.core.id.context.StateEntry;
+import org.olat.core.util.Util;
+import org.olat.course.CourseFactory;
+import org.olat.course.ICourse;
+import org.olat.course.assessment.AssessmentMainController;
+import org.olat.course.assessment.manager.AssessmentNotificationsHandler;
+import org.olat.course.certificate.CertificatesManager;
 import org.olat.group.BusinessGroupService;
 import org.olat.group.model.SearchBusinessGroupParams;
 import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback;
@@ -54,16 +64,47 @@ public class AssessmentCourseOverviewController extends BasicController implemen
 	private final AssessmentCourseStatisticsSmallController statisticsCtrl;
 
 	private Link assessedIdentitiesLink, assessedGroupsLink;
-	
+
+	@Autowired
+	private CertificatesManager certificatesManager;
 	@Autowired
 	private BusinessGroupService businessGroupService;
+	@Autowired
+	private AssessmentNotificationsHandler assessmentNotificationsHandler;
 	
 	public AssessmentCourseOverviewController(UserRequest ureq, WindowControl wControl,
 			RepositoryEntry courseEntry, AssessmentToolSecurityCallback assessmentCallback) {
 		super(ureq, wControl);
+		setTranslator(Util.createPackageTranslator(AssessmentMainController.class, getLocale(), getTranslator()));
 		
 		mainVC = createVelocityContainer("course_overview");
 		
+		ICourse course = CourseFactory.loadCourse(courseEntry);
+		boolean hasAssessableNodes = course.hasAssessableNodes();
+		mainVC.contextPut("hasAssessableNodes", new Boolean(hasAssessableNodes));
+		
+		// assessment changes subscription
+		if (hasAssessableNodes) {
+			SubscriptionContext subsContext = assessmentNotificationsHandler.getAssessmentSubscriptionContext(ureq.getIdentity(), course);
+			if (subsContext != null) {
+				PublisherData pData = assessmentNotificationsHandler.getAssessmentPublisherData(course, wControl.getBusinessControl().getAsString());
+				Controller csc = new ContextualSubscriptionController(ureq, wControl, subsContext, pData);
+				listenTo(csc); // cleanup on dispose
+				mainVC.put("assessmentSubscription", csc.getInitialComponent());
+			}
+		}
+		
+		// certificate subscription
+		SubscriptionContext subsContext = certificatesManager.getSubscriptionContext(course);
+		if (subsContext != null) {
+			String businessPath = wControl.getBusinessControl().getAsString();
+			PublisherData pData = certificatesManager.getPublisherData(course, businessPath);
+			Controller certificateSubscriptionCtrl = new ContextualSubscriptionController(ureq, wControl, subsContext, pData);
+			listenTo(certificateSubscriptionCtrl);
+			mainVC.put("certificationSubscription", certificateSubscriptionCtrl.getInitialComponent());
+		}
+		
+		
 		toReviewCtrl = new AssessmentToReviewSmallController(ureq, getWindowControl(), courseEntry, assessmentCallback);
 		listenTo(toReviewCtrl);
 		mainVC.put("toReview", toReviewCtrl.getInitialComponent());
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java
index 7cb1f228844..a40ecefbb43 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java
@@ -35,6 +35,7 @@ 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.FlexiTableFilter;
+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.elements.table.DefaultFlexiColumnModel;
@@ -49,19 +50,29 @@ 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.wizard.Step;
+import org.olat.core.gui.control.generic.wizard.StepRunnerCallback;
+import org.olat.core.gui.control.generic.wizard.StepsMainRunController;
+import org.olat.core.gui.control.generic.wizard.StepsRunContext;
 import org.olat.core.id.Identity;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
+import org.olat.core.util.mail.MailerResult;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
+import org.olat.course.assessment.AssessedIdentityWrapper;
 import org.olat.course.assessment.AssessmentMainController;
 import org.olat.course.assessment.AssessmentToolManager;
 import org.olat.course.assessment.bulk.PassedCellRenderer;
 import org.olat.course.assessment.model.SearchAssessedIdentityParams;
 import org.olat.course.assessment.ui.tool.AssessmentIdentitiesCourseTableModel.IdentityCourseCols;
 import org.olat.course.certificate.CertificateLight;
+import org.olat.course.certificate.CertificateTemplate;
 import org.olat.course.certificate.CertificatesManager;
+import org.olat.course.certificate.model.CertificateInfos;
+import org.olat.course.certificate.ui.Certificates_1_SelectionStep;
 import org.olat.course.certificate.ui.DownloadCertificateCellRenderer;
+import org.olat.course.config.CourseConfig;
 import org.olat.group.BusinessGroup;
 import org.olat.modules.assessment.model.AssessmentEntryStatus;
 import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback;
@@ -86,9 +97,12 @@ public class AssessmentIdentitiesCourseController extends FormBasicController {
 	private final AssessmentToolSecurityCallback assessmentCallback;
 
 	private Link nextLink, previousLink;
+	private FormLink generateCertificateButton;
 	private FlexiTableElement tableEl;
 	private TooledStackedPanel stackPanel;
 	private AssessmentIdentitiesCourseTableModel usersTableModel;
+	
+	private StepsMainRunController wizardCtrl;
 	private AssessmentIdentityCourseController currentIdentityCtrl;
 
 	@Autowired
@@ -156,10 +170,10 @@ public class AssessmentIdentitiesCourseController extends FormBasicController {
 		filters.add(new FlexiTableFilter(translate("filter.done"), "done"));
 		tableEl.setFilters("", filters);
 
+		ICourse course = CourseFactory.loadCourse(courseEntry);
 		if(assessmentCallback.canAssessBusinessGoupMembers()) {
-			List<BusinessGroup> coachedGroups = null;;
+			List<BusinessGroup> coachedGroups = null;
 			if(assessmentCallback.isAdmin()) {
-				ICourse course = CourseFactory.loadCourse(courseEntry);
 				coachedGroups = course.getCourseEnvironment().getCourseGroupManager().getAllBusinessGroups();
 			} else {
 				coachedGroups = assessmentCallback.getCoachedGroups(); 
@@ -174,6 +188,11 @@ public class AssessmentIdentitiesCourseController extends FormBasicController {
 				tableEl.setExtendedFilterButton(translate("filter.groups"), groupFilters);
 			}
 		}
+		
+		CourseConfig courseConfig = course.getCourseConfig();
+		if(courseConfig.isManualCertificationEnabled()) {
+			generateCertificateButton = uifactory.addFormLink("generate.certificate", formLayout, Link.BUTTON);
+		}
 	}
 	
 	public List<EfficiencyStatementEntry> loadModel(String searchStr, List<FlexiTableFilter> filters, List<FlexiTableFilter> extendedFilters) {
@@ -249,6 +268,20 @@ public class AssessmentIdentitiesCourseController extends FormBasicController {
 		}
 		super.event(ureq, source, event);
 	}
+	
+	
+
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		if(wizardCtrl == source) {
+			if(event == Event.CANCELLED_EVENT || event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
+				getWindowControl().pop();
+				removeAsListenerAndDispose(wizardCtrl);
+				wizardCtrl = null;
+			}
+		}
+		super.event(ureq, source, event);
+	}
 
 	@Override
 	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
@@ -264,11 +297,49 @@ public class AssessmentIdentitiesCourseController extends FormBasicController {
 				FlexiTableSearchEvent ftse = (FlexiTableSearchEvent)event;
 				loadModel(ftse.getSearch(), ftse.getFilters(), ftse.getExtendedFilters());
 			}
+		} else if(generateCertificateButton == source) {
+			doGenerateCertificates(ureq);
 		}
 		
 		super.formInnerEvent(ureq, source, event);
 	}
 	
+	private void doGenerateCertificates(UserRequest ureq) {
+		ICourse course = CourseFactory.loadCourse(courseEntry);
+
+		List<AssessedIdentityWrapper> datas = new ArrayList<>();
+		Certificates_1_SelectionStep start = new Certificates_1_SelectionStep(ureq, courseEntry, datas, course.hasAssessableNodes());
+		StepRunnerCallback finish = new StepRunnerCallback() {
+			@Override
+			public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) {
+				@SuppressWarnings("unchecked")
+				List<CertificateInfos> assessedIdentitiesInfos = (List<CertificateInfos>)runContext.get("infos");
+				if(assessedIdentitiesInfos != null && assessedIdentitiesInfos.size() > 0) {
+					doGenerateCertificates(assessedIdentitiesInfos);
+					return StepsMainRunController.DONE_MODIFIED;
+				}
+				return StepsMainRunController.DONE_UNCHANGED;
+			}
+		};
+		
+		wizardCtrl = new StepsMainRunController(ureq, getWindowControl(), start, finish, null,
+				translate("certificates.wizard.title"), "o_sel_certificates_wizard");
+		listenTo(wizardCtrl);
+		getWindowControl().pushAsModalDialog(wizardCtrl.getInitialComponent());
+	}
+	
+	private void doGenerateCertificates(List<CertificateInfos> assessedIdentitiesInfos) {
+		ICourse course = CourseFactory.loadCourse(courseEntry);
+		Long templateKey = course.getCourseConfig().getCertificateTemplate();
+		CertificateTemplate template = null;
+		if(templateKey != null) {
+			template = certificatesManager.getTemplateById(templateKey);
+		}
+		
+		MailerResult result = new MailerResult();
+		certificatesManager.generateCertificates(assessedIdentitiesInfos, courseEntry, template, result);
+	}
+	
 	private void doNext(UserRequest ureq) {
 		stackPanel.popController(currentIdentityCtrl);
 		
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeController.java
index c47cd0a704b..946205a2253 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeController.java
@@ -26,6 +26,7 @@ import java.util.Map;
 
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.basesecurity.BaseSecurityModule;
+import org.olat.basesecurity.Group;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.form.flexible.FormItem;
@@ -42,6 +43,7 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTable
 import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent;
 import org.olat.core.gui.components.link.Link;
 import org.olat.core.gui.components.link.LinkFactory;
+import org.olat.core.gui.components.stack.BreadcrumbPanelAware;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
 import org.olat.core.gui.components.stack.TooledStackedPanel.Align;
 import org.olat.core.gui.control.Controller;
@@ -58,6 +60,7 @@ import org.olat.course.assessment.bulk.PassedCellRenderer;
 import org.olat.course.assessment.model.SearchAssessedIdentityParams;
 import org.olat.course.assessment.ui.tool.AssessmentIdentitiesCourseNodeTableModel.IdentityCourseElementCols;
 import org.olat.course.nodes.AssessableCourseNode;
+import org.olat.course.nodes.AssessmentToolOptions;
 import org.olat.course.nodes.CourseNode;
 import org.olat.course.nodes.CourseNodeFactory;
 import org.olat.group.BusinessGroup;
@@ -65,6 +68,7 @@ import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.modules.assessment.model.AssessmentEntryStatus;
 import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback;
 import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryService;
 import org.olat.user.UserManager;
 import org.olat.user.propertyhandlers.UserPropertyHandler;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -77,6 +81,7 @@ import org.springframework.beans.factory.annotation.Autowired;
  */
 public class AssessmentIdentitiesCourseNodeController extends FormBasicController {
 
+	private final BusinessGroup group;
 	private final CourseNode courseNode;
 	private final RepositoryEntry courseEntry;
 	private final RepositoryEntry referenceEntry;
@@ -98,6 +103,8 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 	@Autowired
 	private BaseSecurityModule securityModule;
 	@Autowired
+	private RepositoryService repositoryService;
+	@Autowired
 	private AssessmentToolManager assessmentToolManager;
 	
 	public AssessmentIdentitiesCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
@@ -106,6 +113,7 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 		setTranslator(Util.createPackageTranslator(AssessmentMainController.class, getLocale(), getTranslator()));
 		setTranslator(userManager.getPropertyHandlerTranslator(getTranslator()));
 		
+		this.group = null;
 		this.courseNode = courseNode;
 		this.stackPanel = stackPanel;
 		this.courseEntry = courseEntry;
@@ -121,7 +129,7 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 		userPropertyHandlers = userManager.getUserPropertyHandlersFor(AssessmentToolConstants.usageIdentifyer, isAdministrativeUser);
 		
 		initForm(ureq);
-		updateModel(null, null, null);
+		updateModel(ureq, null, null, null);
 	}
 
 	@Override
@@ -199,7 +207,7 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 		}
 	}
 	
-	private void updateModel(String searchString, List<FlexiTableFilter> filters, List<FlexiTableFilter> extendedFilters) {
+	private void updateModel(UserRequest ureq, String searchString, List<FlexiTableFilter> filters, List<FlexiTableFilter> extendedFilters) {
 		SearchAssessedIdentityParams params = new SearchAssessedIdentityParams(courseEntry, referenceEntry, courseNode.getIdent(), assessmentCallback);
 		
 		List<AssessmentEntryStatus> assessmentStatus = null;
@@ -241,6 +249,51 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 		}
 		usersTableModel.setObjects(rows);
 		tableEl.reloadData();
+
+		List<String> toolCmpNames = new ArrayList<>();
+		if(courseNode instanceof AssessableCourseNode) {
+			AssessableCourseNode acn = (AssessableCourseNode)courseNode;
+			ICourse course = CourseFactory.loadCourse(courseEntry);
+			AssessmentToolOptions options = new AssessmentToolOptions();
+			if(group == null) {
+				options.setIdentities(assessedIdentities);
+				fillAlternativeToAssessableIdentityList(options);
+			} else {
+				options.setGroup(group);
+			}
+			
+			//TODO qti filter by group?
+			List<Controller> tools = acn.createAssessmentTools(ureq, getWindowControl(), stackPanel, course.getCourseEnvironment(), options);
+			int count = 0;
+			if(tools.size() > 0) {
+				for(Controller tool:tools) {
+					listenTo(tool);
+					String toolCmpName = "ctrl_" + (count++);
+					flc.put(toolCmpName, tool.getInitialComponent());
+					toolCmpNames.add(toolCmpName);
+					if(tool instanceof BreadcrumbPanelAware) {
+						((BreadcrumbPanelAware)tool).setBreadcrumbPanel(stackPanel);
+					}
+				}
+			}
+			
+		}
+		flc.contextPut("toolCmpNames", toolCmpNames);
+	}
+	
+	private void fillAlternativeToAssessableIdentityList(AssessmentToolOptions options) {
+		List<Group> baseGroups = new ArrayList<>();
+		if((assessmentCallback.canAssessRepositoryEntryMembers() && assessmentCallback.getCoachedGroups().isEmpty())
+				|| assessmentCallback.canAssessNonMembers()) {
+			baseGroups.add(repositoryService.getDefaultGroup(courseEntry));
+		}
+		if(assessmentCallback.getCoachedGroups().size() > 0) {
+			for(BusinessGroup coachedGroup:assessmentCallback.getCoachedGroups()) {
+				baseGroups.add(coachedGroup.getBaseGroup());
+			}
+		}
+		options.setAlternativeToIdentities(baseGroups, assessmentCallback.canAssessNonMembers());
+
 	}
 
 	@Override
@@ -275,7 +328,7 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 				}
 			} else if(event instanceof FlexiTableSearchEvent) {
 				FlexiTableSearchEvent ftse = (FlexiTableSearchEvent)event;
-				updateModel(ftse.getSearch(), ftse.getFilters(), ftse.getExtendedFilters());
+				updateModel(ureq, ftse.getSearch(), ftse.getFilters(), ftse.getExtendedFilters());
 			}
 		}
 		
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java
index adcc94063f5..0ccf2a1a422 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java
@@ -26,6 +26,7 @@ import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.link.Link;
 import org.olat.core.gui.components.link.LinkFactory;
 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;
@@ -38,6 +39,8 @@ import org.olat.core.id.context.StateEntry;
 import org.olat.core.util.Util;
 import org.olat.core.util.resource.OresHelper;
 import org.olat.course.assessment.AssessmentMainController;
+import org.olat.course.assessment.EfficiencyStatementAssessmentController;
+import org.olat.course.assessment.bulk.BulkAssessmentOverviewController;
 import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback;
 import org.olat.repository.RepositoryEntry;
 
@@ -53,10 +56,13 @@ public class AssessmentToolController extends MainLayoutBasicController implemen
 	private RepositoryEntry courseEntry;
 	private final AssessmentToolSecurityCallback assessmentCallback;
 	
-	private Link usersLink;
+	private Link usersLink, efficiencyStatementsLink, bulkAssessmentLink;
 	private final TooledStackedPanel stackPanel;
+	
 	private AssessmentCourseOverviewController overviewCtrl;
 	private AssessmentIdentitiesCourseTreeController currentCtl;
+	private BulkAssessmentOverviewController bulkAssessmentOverviewCtrl;
+	private EfficiencyStatementAssessmentController efficiencyStatementCtrl;
 	
 	public AssessmentToolController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
 			RepositoryEntry courseEntry, AssessmentToolSecurityCallback assessmentCallback) {
@@ -74,8 +80,16 @@ public class AssessmentToolController extends MainLayoutBasicController implemen
 	
 	public void initToolbar() {
 		usersLink = LinkFactory.createToolLink("users", translate("users"), this, "o_icon_user");
+		usersLink.setElementCssClass("o_sel_assessment_tool_users");
 		stackPanel.addTool(usersLink);
 		
+		efficiencyStatementsLink = LinkFactory.createToolLink("efficiencyStatements", translate("menu.efficiency.statment"), this, "o_icon_certificate");
+		efficiencyStatementsLink.setElementCssClass("o_sel_assessment_tool_efficiency_statements");
+		stackPanel.addTool(efficiencyStatementsLink, Align.right);
+		
+		bulkAssessmentLink = LinkFactory.createToolLink("bulkAssessment", translate("menu.bulkfocus"), this, "o_icon_group");
+		bulkAssessmentLink.setElementCssClass("o_sel_assessment_tool_bulk");
+		stackPanel.addTool(bulkAssessmentLink, Align.right);
 	}
 
 	@Override
@@ -93,7 +107,14 @@ public class AssessmentToolController extends MainLayoutBasicController implemen
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
 		if (source == usersLink) {
+			cleanUp();
 			doSelectUsersView(ureq);
+		} else if(efficiencyStatementsLink == source) {
+			cleanUp();
+			doEfficiencyStatementView(ureq);
+		} else if(bulkAssessmentLink == source) {
+			cleanUp();
+			doBulkAssessmentView(ureq);
 		}
 	}
 
@@ -106,10 +127,27 @@ public class AssessmentToolController extends MainLayoutBasicController implemen
 		}
 		super.event(ureq, source, event);
 	}
+	
+	private void cleanUp() {
+		removeAsListenerAndDispose(bulkAssessmentOverviewCtrl);
+		removeAsListenerAndDispose(currentCtl);
+		bulkAssessmentOverviewCtrl = null;
+		currentCtl = null;
+	}
+	
+	private void doBulkAssessmentView(UserRequest ureq) {
+		bulkAssessmentOverviewCtrl = new BulkAssessmentOverviewController(ureq, getWindowControl(), courseEntry);
+		listenTo(bulkAssessmentOverviewCtrl);
+		stackPanel.pushController(translate("menu.bulkfocus"), bulkAssessmentOverviewCtrl);
+	}
+	
+	private void doEfficiencyStatementView(UserRequest ureq) {
+		efficiencyStatementCtrl = new EfficiencyStatementAssessmentController(ureq, getWindowControl(), courseEntry);
+		listenTo(efficiencyStatementCtrl);
+		stackPanel.pushController(translate("menu.efficiency.statment"), efficiencyStatementCtrl);
+	}
 
 	private void doSelectUsersView(UserRequest ureq) {
-		removeAsListenerAndDispose(currentCtl);
-		
 		OLATResourceable ores = OresHelper.createOLATResourceableInstance("Users", 0l);
 		WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
 		addToHistory(ureq, bwControl);
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_content/course_overview.html b/src/main/java/org/olat/course/assessment/ui/tool/_content/course_overview.html
index 58a9bd40933..51a92af102b 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/_content/course_overview.html
+++ b/src/main/java/org/olat/course/assessment/ui/tool/_content/course_overview.html
@@ -1,4 +1,9 @@
-<h2>$r.translate("assessment.tool.overview")</h2>
+$r.contextHelpWithWrapper("Using Course Tools#_bewertungswerkzeug")
+<h2><i class="o_icon o_icon_assessment_tool"> </i>  $r.translate("assessment.tool.overview")</h2>
+#if(!$hasAssessableNodes)
+	<p class="o_warning">$r.translate("index.noAssessableNodes")</p>
+#end
+<p class="o_info">$r.translate("index.intro")</p>
 <div class="row o_block">
 	<div class="col-sm-6">
 		#if($r.available("assessed.identities"))
@@ -22,4 +27,26 @@
 			$r.render("statistics")
 		#end
 	</div>
-</div>
\ No newline at end of file
+</div>
+<div class="row">
+	#if($r.available("assessmentSubscription") || $r.available("certificationSubscription"))
+	<div class="col-sm-6">
+		<h4>$r.translate("index.notifications.title")</h4>
+		<table class="table">
+			#if($r.available("assessmentSubscription"))
+			<tr>
+				<th>$r.translate("index.notifications.assessment")</th>
+				<td>$r.render("assessmentSubscription")</td>
+			</tr>
+			#end
+			#if($r.available("certificationSubscription"))
+			<tr>
+				<th>$r.translate("index.notifications.certificate")</th>
+				<td>$r.render("certificationSubscription")</td>
+			</tr>
+			#end
+		</table>
+	</div>
+	<div class="col-sm-6"></div>
+	#end
+</div>
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_course.html b/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_course.html
index bade9402acd..4b9158c373a 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_course.html
+++ b/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_course.html
@@ -1 +1,7 @@
-$r.render("table")
\ No newline at end of file
+$r.render("table")
+
+#if ($r.available("generate.certificate"))
+<div class="o_button_group">
+	$r.render("generate.certificate")
+</div>
+#end
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_courseelement.html b/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_courseelement.html
index fdf31362f35..85f0139cfd0 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_courseelement.html
+++ b/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_courseelement.html
@@ -1,2 +1,10 @@
 <h2><i class="o_icon $courseNodeCssClass"> </i> $r.escapeHtml($courseNodeTitle)</h2>
-$r.render("table")
\ No newline at end of file
+$r.render("table")
+
+#if ($toolCmpNames)
+	<div class="o_button_group">
+	#foreach($toolCmpName in $toolCmpNames)
+		$r.render($toolCmpName)
+	#end
+	</div>
+#end
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties
index c6061631de5..cbb6dcae32c 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties
@@ -31,3 +31,5 @@ filter.inProgress=Gestartet
 filter.inReview=Korrigieren
 filter.done=Bewertet
 filter.groups=Gruppen
+certificates.wizard.title=$org.olat.course.certificate.ui\:certificates.wizard.title
+generate.certificate=$org.olat.course.certificate.ui\:generate.certificate
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties
index ec501f9f433..c31d34757e5 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties
@@ -1 +1,3 @@
 #Fri Feb 06 10:03:07 CET 2015
+certificates.wizard.title=$org.olat.course.certificate.ui\:certificates.wizard.title
+generate.certificate=$org.olat.course.certificate.ui\:generate.certificate
diff --git a/src/main/java/org/olat/course/certificate/ui/CertificatesSelectionController.java b/src/main/java/org/olat/course/certificate/ui/CertificatesSelectionController.java
index ca9d2797e56..2e99e2ebd7c 100644
--- a/src/main/java/org/olat/course/certificate/ui/CertificatesSelectionController.java
+++ b/src/main/java/org/olat/course/certificate/ui/CertificatesSelectionController.java
@@ -38,7 +38,6 @@ import org.olat.core.gui.control.generic.wizard.StepFormBasicController;
 import org.olat.core.gui.control.generic.wizard.StepsEvent;
 import org.olat.core.gui.control.generic.wizard.StepsRunContext;
 import org.olat.core.id.Identity;
-import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.Roles;
 import org.olat.core.util.Util;
 import org.olat.course.CourseFactory;
@@ -50,6 +49,7 @@ import org.olat.course.assessment.bulk.PassedCellRenderer;
 import org.olat.course.certificate.model.CertificateInfos;
 import org.olat.course.nodes.CourseNode;
 import org.olat.course.run.scoring.ScoreEvaluation;
+import org.olat.repository.RepositoryEntry;
 import org.olat.user.UserManager;
 import org.olat.user.propertyhandlers.UserPropertyHandler;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -66,7 +66,7 @@ public class CertificatesSelectionController extends StepFormBasicController {
 	private CertificatesSelectionDataModel tableModel;
 	
 	private final boolean hasAssessableNodes;
-	private final OLATResourceable courseOres;
+	private final RepositoryEntry courseEntry;
 	private final boolean isAdministrativeUser;
 	private final List<AssessedIdentityWrapper> datas;
 	
@@ -76,14 +76,14 @@ public class CertificatesSelectionController extends StepFormBasicController {
 	private BaseSecurityModule securityModule;
 	
 	public CertificatesSelectionController(UserRequest ureq, WindowControl wControl,
-			Form rootForm, StepsRunContext runContext, OLATResourceable courseOres,
+			Form rootForm, StepsRunContext runContext, RepositoryEntry courseEntry,
 			List<AssessedIdentityWrapper> datas, boolean hasAssessableNodes) {
 		super(ureq, wControl, rootForm, runContext, LAYOUT_BAREBONE, null);
 		setTranslator(Util.createPackageTranslator(UserPropertyHandler.class, getLocale(), getTranslator()));
 		setTranslator(Util.createPackageTranslator(AssessmentMainController.class, getLocale(), getTranslator()));
 		
 		this.datas = datas;
-		this.courseOres = courseOres;
+		this.courseEntry = courseEntry;
 		this.hasAssessableNodes = hasAssessableNodes;
 		Roles roles = ureq.getUserSession().getRoles();
 		isAdministrativeUser = securityModule.isUserAllowedAdminProps(roles);
@@ -106,10 +106,8 @@ public class CertificatesSelectionController extends StepFormBasicController {
 		for (int i = 0; i < userPropertyHandlers.size(); i++) {
 			UserPropertyHandler userPropertyHandler	= userPropertyHandlers.get(i);
 			boolean visible = userManager.isMandatoryUserProperty(AssessedIdentitiesTableDataModel.usageIdentifyer , userPropertyHandler);
-			if(visible) {
-				resultingPropertyHandlers.add(userPropertyHandler);
-				columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(userPropertyHandler.i18nColumnDescriptorLabelKey(), colPos++));
-			}
+			resultingPropertyHandlers.add(userPropertyHandler);
+			columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(visible, userPropertyHandler.i18nColumnDescriptorLabelKey(), colPos++, false, null));
 		}
 		
 		if(hasAssessableNodes) {
@@ -120,7 +118,7 @@ public class CertificatesSelectionController extends StepFormBasicController {
 		tableModel = new CertificatesSelectionDataModel(columnsModel, resultingPropertyHandlers);
 
 		Set<Integer> preselectedRows = new HashSet<>();
-		ICourse course = CourseFactory.loadCourse(courseOres);
+		ICourse course = CourseFactory.loadCourse(courseEntry);
 		CourseNode rootNode = course.getRunStructure().getRootNode();
 		List<CertificateInfos> infos = new ArrayList<CertificateInfos>(datas.size());
 		
diff --git a/src/main/java/org/olat/course/certificate/ui/CertificatesWizardController.java b/src/main/java/org/olat/course/certificate/ui/CertificatesWizardController.java
index 46ed0f81080..8b205e87535 100644
--- a/src/main/java/org/olat/course/certificate/ui/CertificatesWizardController.java
+++ b/src/main/java/org/olat/course/certificate/ui/CertificatesWizardController.java
@@ -33,7 +33,6 @@ import org.olat.core.gui.control.generic.wizard.Step;
 import org.olat.core.gui.control.generic.wizard.StepRunnerCallback;
 import org.olat.core.gui.control.generic.wizard.StepsMainRunController;
 import org.olat.core.gui.control.generic.wizard.StepsRunContext;
-import org.olat.core.id.OLATResourceable;
 import org.olat.core.util.mail.MailerResult;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
@@ -57,18 +56,18 @@ public class CertificatesWizardController extends BasicController {
 	private StepsMainRunController wizardCtrl;
 	
 	private final boolean hasAssessableNodes;
-	private final OLATResourceable courseOres;
+	private final RepositoryEntry courseEntry;
 	private final AssessedIdentitiesTableDataModel dataModel;
 	
 	@Autowired
 	private CertificatesManager certificatesManager;
 	
 	public CertificatesWizardController(UserRequest ureq, WindowControl wControl,
-			AssessedIdentitiesTableDataModel dataModel, OLATResourceable courseOres, boolean hasAssessableNodes) {
+			AssessedIdentitiesTableDataModel dataModel, RepositoryEntry courseEntry, boolean hasAssessableNodes) {
 		super(ureq, wControl);
 		
 		this.dataModel = dataModel;
-		this.courseOres = courseOres;
+		this.courseEntry = courseEntry;
 		this.hasAssessableNodes = hasAssessableNodes;
 		
 		startButton = LinkFactory.createButton("generate.certificate", null, this);
@@ -106,7 +105,7 @@ public class CertificatesWizardController extends BasicController {
 
 	private void doStartWizard(UserRequest ureq) {
 		List<AssessedIdentityWrapper> datas = dataModel.getObjects();
-		Certificates_1_SelectionStep start = new Certificates_1_SelectionStep(ureq, courseOres, datas, hasAssessableNodes);
+		Certificates_1_SelectionStep start = new Certificates_1_SelectionStep(ureq, courseEntry, datas, hasAssessableNodes);
 		StepRunnerCallback finish = new StepRunnerCallback() {
 			@Override
 			public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) {
@@ -127,8 +126,7 @@ public class CertificatesWizardController extends BasicController {
 	}
 	
 	private void doGenerateCertificates(List<CertificateInfos> assessedIdentitiesInfos) {
-		ICourse course = CourseFactory.loadCourse(courseOres);
-		RepositoryEntry resource = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
+		ICourse course = CourseFactory.loadCourse(courseEntry);
 		Long templateKey = course.getCourseConfig().getCertificateTemplate();
 		CertificateTemplate template = null;
 		if(templateKey != null) {
@@ -136,6 +134,6 @@ public class CertificatesWizardController extends BasicController {
 		}
 		
 		MailerResult result = new MailerResult();
-		certificatesManager.generateCertificates(assessedIdentitiesInfos, resource, template, result);
+		certificatesManager.generateCertificates(assessedIdentitiesInfos, courseEntry, template, result);
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/certificate/ui/Certificates_1_SelectionStep.java b/src/main/java/org/olat/course/certificate/ui/Certificates_1_SelectionStep.java
index b86d040922f..b6baf30649d 100644
--- a/src/main/java/org/olat/course/certificate/ui/Certificates_1_SelectionStep.java
+++ b/src/main/java/org/olat/course/certificate/ui/Certificates_1_SelectionStep.java
@@ -28,8 +28,8 @@ import org.olat.core.gui.control.generic.wizard.BasicStep;
 import org.olat.core.gui.control.generic.wizard.PrevNextFinishConfig;
 import org.olat.core.gui.control.generic.wizard.StepFormController;
 import org.olat.core.gui.control.generic.wizard.StepsRunContext;
-import org.olat.core.id.OLATResourceable;
 import org.olat.course.assessment.AssessedIdentityWrapper;
+import org.olat.repository.RepositoryEntry;
 
 /**
  * 
@@ -40,14 +40,14 @@ import org.olat.course.assessment.AssessedIdentityWrapper;
 public class Certificates_1_SelectionStep extends BasicStep {
 	
 	private final boolean hasAssessableNodes;
-	private final OLATResourceable courseOres;
+	private final RepositoryEntry courseEntry;
 	private final List<AssessedIdentityWrapper> datas;
 	
-	public Certificates_1_SelectionStep(UserRequest ureq, OLATResourceable courseOres,
+	public Certificates_1_SelectionStep(UserRequest ureq, RepositoryEntry courseEntry,
 			List<AssessedIdentityWrapper> datas, boolean hasAssessableNodes) {
 		super(ureq);
 		this.datas = datas;
-		this.courseOres = courseOres;
+		this.courseEntry = courseEntry;
 		this.hasAssessableNodes = hasAssessableNodes;
 		setNextStep(new Certificates_2_OverviewStep(ureq, hasAssessableNodes));
 		setI18nTitleAndDescr("certificates.wizard.select", "certificates.wizard.select");
@@ -61,7 +61,7 @@ public class Certificates_1_SelectionStep extends BasicStep {
 	@Override
 	public StepFormController getStepController(UserRequest ureq, WindowControl wControl, StepsRunContext runContext, Form form) {
 		CertificatesSelectionController selectCtrl = new CertificatesSelectionController(ureq, wControl, form, runContext,
-				courseOres, datas, hasAssessableNodes);
+				courseEntry, datas, hasAssessableNodes);
 		return selectCtrl;
 	}
 }
-- 
GitLab