From e05360bdd9772555bf3c6f30f58f7faff4fbfffe Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Wed, 3 Dec 2014 11:46:18 +0100
Subject: [PATCH] OO-1338: ask to publish the course after closing the editor

---
 .../java/org/olat/catalog/CatalogManager.java |   2 +-
 .../fullWebApp/BaseFullWebappController.java  |   2 +-
 .../stack/BreadcrumbedStackedPanel.java       |   2 +-
 .../gui/control/VetoableCloseController.java  |   4 +-
 .../course/editor/EditorMainController.java   |  90 +++-
 .../course/editor/QuickPublishController.java | 124 ++++++
 .../course/editor/_content/quick_publish.html |   6 +
 .../editor/_i18n/LocalStrings_de.properties   |   5 +
 .../olat/course/nodes/co/CORunController.java |   1 -
 .../course/run/CourseRuntimeController.java   | 410 ++++++++++++------
 .../olat/ims/qti/QTIRuntimeController.java    |  20 +-
 .../qti/editor/QTIEditorMainController.java   |   6 +-
 .../ui/RepositoryEntryRuntimeController.java  |  10 +-
 .../ui/WebDocumentRunController.java          |  19 +
 14 files changed, 535 insertions(+), 166 deletions(-)
 create mode 100644 src/main/java/org/olat/course/editor/QuickPublishController.java
 create mode 100644 src/main/java/org/olat/course/editor/_content/quick_publish.html

diff --git a/src/main/java/org/olat/catalog/CatalogManager.java b/src/main/java/org/olat/catalog/CatalogManager.java
index 2fc431cf777..76dcef6b7d3 100644
--- a/src/main/java/org/olat/catalog/CatalogManager.java
+++ b/src/main/java/org/olat/catalog/CatalogManager.java
@@ -394,7 +394,7 @@ public class CatalogManager extends BasicManager implements UserDataDeletable, I
 	 * @param repoEntry
 	 * @return List of catalog entries
 	 */
-	public List<CatalogEntry> getCatalogCategoriesFor(RepositoryEntry repoEntry) {
+	public List<CatalogEntry> getCatalogCategoriesFor(RepositoryEntryRef repoEntry) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select parentCei from ").append(CatalogEntryImpl.class.getName()).append(" as cei ")
 		  .append(" inner join cei.ownerGroup ownerGroup ")
diff --git a/src/main/java/org/olat/core/commons/fullWebApp/BaseFullWebappController.java b/src/main/java/org/olat/core/commons/fullWebApp/BaseFullWebappController.java
index ad498809229..4618c931640 100644
--- a/src/main/java/org/olat/core/commons/fullWebApp/BaseFullWebappController.java
+++ b/src/main/java/org/olat/core/commons/fullWebApp/BaseFullWebappController.java
@@ -972,7 +972,7 @@ public class BaseFullWebappController extends BasicController implements ChiefCo
 			// rembember current dtab, and swap to the temporary one
 			DTab reTab = curDTab;
 			doActivateDTab(delt);
-			boolean immediateClose = vcc.requestForClose();
+			boolean immediateClose = vcc.requestForClose(ureq);
 			if (!immediateClose) {
 				return;
 			} else {
diff --git a/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanel.java b/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanel.java
index 2d497956132..623331e79f6 100644
--- a/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanel.java
+++ b/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanel.java
@@ -175,7 +175,7 @@ public class BreadcrumbedStackedPanel extends Panel implements StackedPanel, Bre
 			Controller controllerToPop = getControllerToPop(source);
 			//part of a hack for QTI editor
 			if(controllerToPop instanceof VetoableCloseController
-					&& !((VetoableCloseController)controllerToPop).requestForClose()) {
+					&& !((VetoableCloseController)controllerToPop).requestForClose(ureq)) {
 				// not my problem anymore, I have done what I can
 				fireEvent(ureq, new VetoPopEvent());
 				return;
diff --git a/src/main/java/org/olat/core/gui/control/VetoableCloseController.java b/src/main/java/org/olat/core/gui/control/VetoableCloseController.java
index b0a5c140cd8..b3b8d1e5ac2 100644
--- a/src/main/java/org/olat/core/gui/control/VetoableCloseController.java
+++ b/src/main/java/org/olat/core/gui/control/VetoableCloseController.java
@@ -26,6 +26,8 @@
 
 package org.olat.core.gui.control;
 
+import org.olat.core.gui.UserRequest;
+
 
 /**
  * Initial Date:  Mar 7, 2005
@@ -37,5 +39,5 @@ public interface VetoableCloseController extends Controller {
 	/**
 	 * @return true if this controller can be closed immediately (and the closecallback will not be used), false if the close will be delayed
 	 */
-	public boolean requestForClose();
+	public boolean requestForClose(UserRequest ureq);
 }
diff --git a/src/main/java/org/olat/course/editor/EditorMainController.java b/src/main/java/org/olat/course/editor/EditorMainController.java
index 8a9f5ad4131..705a6481052 100644
--- a/src/main/java/org/olat/course/editor/EditorMainController.java
+++ b/src/main/java/org/olat/course/editor/EditorMainController.java
@@ -52,6 +52,7 @@ import org.olat.core.gui.components.tree.TreeNode;
 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.VetoableCloseController;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.MainLayoutBasicController;
 import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowController;
@@ -102,6 +103,7 @@ import org.olat.course.nodes.cl.ui.wizard.CheckList_1_CheckboxStep;
 import org.olat.course.run.preview.PreviewConfigController;
 import org.olat.course.tree.CourseEditorTreeModel;
 import org.olat.course.tree.CourseEditorTreeNode;
+import org.olat.course.tree.PublishTreeModel;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.repository.ui.RepositoryEntryRuntimeController.ToolbarAware;
@@ -120,7 +122,7 @@ import org.olat.util.logging.activity.LoggingResourceable;
  * @author Felix Jost
  * @author BPS (<a href="http://www.bps-system.de/">BPS Bildungsportal Sachsen GmbH</a>)
  */
-public class EditorMainController extends MainLayoutBasicController implements GenericEventListener, ToolbarAware {
+public class EditorMainController extends MainLayoutBasicController implements GenericEventListener, VetoableCloseController, ToolbarAware {
 	private static final String VELOCITY_ROOT = Util.getPackageVelocityRoot(EditorMainController.class);
 	
 	protected static final String TB_ACTION = "o_tb_do_";
@@ -151,6 +153,8 @@ public class EditorMainController extends MainLayoutBasicController implements G
 	private static final String NLS_DELETENODE_ERROR_ROOTNODE = "deletenode.error.rootnode";
 	private static final String NLS_MOVECOPYNODE_ERROR_SELECTFIRST = "movecopynode.error.selectfirst";
 	private static final String NLS_MOVECOPYNODE_ERROR_ROOTNODE = "movecopynode.error.rootnode";
+	
+	protected static final Event MANUAL_PUBLISH = new Event("manual-publish");
 
 	private MenuTree menuTree;
 	private VelocityContainer main;
@@ -168,6 +172,7 @@ public class EditorMainController extends MainLayoutBasicController implements G
 	private AlternativeCourseNodeController alternateCtr;
 	private EditorStatusController statusCtr;
 	private ChooseNodeController chooseNodeTypeCtr;
+	private QuickPublishController quickPublishCtr;
 	
 	private LockResult lockEntry;
 	
@@ -307,7 +312,7 @@ public class EditorMainController extends MainLayoutBasicController implements G
 				previewLink = LinkFactory.createToolLink(CMD_COURSEPREVIEW, translate(NLS_COMMAND_COURSEPREVIEW), this, "o_icon_preview");
 				publishLink = LinkFactory.createToolLink(CMD_PUBLISH, translate(NLS_COMMAND_PUBLISH), this, "o_icon_publish");
 				publishLink.setElementCssClass("o_sel_course_editor_publish");
-				
+
 				// validate course and update course status
 				euce.getCourseEditorEnv().validateCourse();
 				StatusDescription[] courseStatus = euce.getCourseEditorEnv().getCourseStatus();
@@ -342,7 +347,18 @@ public class EditorMainController extends MainLayoutBasicController implements G
 		stackPanel.addTool(previewLink, Align.right);
 		stackPanel.addTool(publishLink, Align.right);
 	}
-	
+
+	@Override
+	public boolean requestForClose(UserRequest ureq) {
+		boolean immediateClose = true;
+		ICourse course = CourseFactory.loadCourse(ores.getResourceableId());
+		if(hasPublishableChanges(course)) {
+			doQuickPublish(ureq, course);
+			immediateClose = false;
+		}
+		return immediateClose;
+	}
+
 	/**
 	 * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest,
 	 *      org.olat.core.gui.components.Component, org.olat.core.gui.control.Event)
@@ -374,7 +390,7 @@ public class EditorMainController extends MainLayoutBasicController implements G
 			} else if(previewLink == source) {
 				launchPreview(ureq, course);
 			} else if(publishLink == source) {
-				launchPublishingWizard(ureq, course);
+				launchPublishingWizard(ureq, course, false);
 			} else if(closeLink == source) {
 				doReleaseEditLock();
 				fireEvent(ureq, Event.DONE_EVENT);
@@ -589,20 +605,29 @@ public class EditorMainController extends MainLayoutBasicController implements G
 			}
 		} else if (source == publishStepsController) {
 			getWindowControl().pop();
+			
+			Object requestOnClose = publishStepsController.getRunContext().get("requestOnClose");
 			removeAsListenerAndDispose(publishStepsController);
 			publishStepsController = null;
 			// reset to root node... may have published a deleted node -> this
 			// resets the view
-			cetm = course.getEditorTreeModel();
-			menuTree.setTreeModel(cetm);
-			String rootNodeIdent = menuTree.getTreeModel().getRootNode().getIdent();
-			menuTree.setSelectedNodeId(rootNodeIdent);
-			updateViewForSelectedNodeId(ureq, rootNodeIdent);
-			if(event == Event.CHANGED_EVENT){					
-				showInfo("pbl.success");
-				// do logging
-				ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_EDITOR_PUBLISHED, getClass());
-			}//else Event.DONE -> nothing changed / else Event.CANCELLED -> cancelled wizard	
+			//else Event.DONE -> nothing changed / else Event.CANCELLED -> cancelled wizard	
+			updateAfterPublishing(ureq, course, event == Event.CHANGED_EVENT);
+			if(Boolean.TRUE.equals(requestOnClose)) {
+				fireEvent(ureq, Event.DONE_EVENT);
+			}
+		} else if(source == quickPublishCtr) {
+			if(event == Event.CANCELLED_EVENT) {
+				cmc.deactivate();
+				fireEvent(ureq, Event.DONE_EVENT);
+			} else if(event == MANUAL_PUBLISH) {
+				cmc.deactivate();
+				launchPublishingWizard(ureq, course, true);
+			} else if(event == Event.CHANGED_EVENT) {
+				updateAfterPublishing(ureq, course, event == Event.CHANGED_EVENT);
+				cmc.deactivate();
+				fireEvent(ureq, Event.DONE_EVENT);
+			}
 		} else if (source == previewController) {
 			if (event == Event.DONE_EVENT) {
 				// no need to deactivate preview controller, already done internally
@@ -711,6 +736,19 @@ public class EditorMainController extends MainLayoutBasicController implements G
 		cmc = null;
 	}
 	
+	private void updateAfterPublishing(UserRequest ureq, ICourse course, boolean changed) {
+		cetm = course.getEditorTreeModel();
+		menuTree.setTreeModel(cetm);
+		String rootNodeIdent = menuTree.getTreeModel().getRootNode().getIdent();
+		menuTree.setSelectedNodeId(rootNodeIdent);
+		updateViewForSelectedNodeId(ureq, rootNodeIdent);
+		if(changed) {					
+			showInfo("pbl.success");
+			// do logging
+			ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_EDITOR_PUBLISHED, getClass());
+		}
+	}
+	
 	private void doMove(UserRequest ureq, ICourse course, boolean copy) {
 		if(moveCopyController != null) return;
 		
@@ -734,6 +772,18 @@ public class EditorMainController extends MainLayoutBasicController implements G
 		cmc.activate();
 	}
 	
+	private void doQuickPublish(UserRequest ureq, ICourse course) {
+		removeAsListenerAndDispose(quickPublishCtr);
+		removeAsListenerAndDispose(cmc);
+		
+		quickPublishCtr = new QuickPublishController(ureq, getWindowControl(), course);
+		listenTo(quickPublishCtr);
+		
+		cmc = new CloseableModalController(getWindowControl(), "close", quickPublishCtr.getInitialComponent(),
+				true, translate("pbl.quick.title"));
+		listenTo(cmc);
+		cmc.activate();
+	}
 	
 	private void doDelete(ICourse course, String ident) {
 		CourseNode activeNode = cetm.getCourseNode(ident);
@@ -985,7 +1035,7 @@ public class EditorMainController extends MainLayoutBasicController implements G
 		}
 	}
 
-	private void launchPublishingWizard(UserRequest ureq, ICourse course) {
+	private void launchPublishingWizard(UserRequest ureq, ICourse course, boolean requestOnClose) {
 		if(publishStepsController != null) return;//ignore enter
 		
 		/*
@@ -1062,8 +1112,10 @@ public class EditorMainController extends MainLayoutBasicController implements G
 			}
 		};
 
-		publishStepsController = new StepsMainRunController(ureq, getWindowControl(), start, finish, null, translate("publish.wizard.title"), "o_sel_course_publish_wizard");
+		publishStepsController = new StepsMainRunController(ureq, getWindowControl(), start, finish, null,
+				translate("publish.wizard.title"), "o_sel_course_publish_wizard");
 		listenTo(publishStepsController);
+		publishStepsController.getRunContext().put("requestOnClose", requestOnClose);
 		getWindowControl().pushAsModalDialog(publishStepsController.getInitialComponent());
 	}
 	
@@ -1105,6 +1157,12 @@ public class EditorMainController extends MainLayoutBasicController implements G
 	public LockResult getLockEntry() {
 		return lockEntry;
 	}
+	
+	public boolean hasPublishableChanges(ICourse course) {
+		PublishProcess publishProcess = PublishProcess.getInstance(course, cetm, getLocale());
+		PublishTreeModel publishTreeModel = publishProcess.getPublishTreeModel();
+		return publishTreeModel.hasPublishableChanges();
+	}
 
 	protected void doDispose() {
 		ICourse course = CourseFactory.loadCourse(ores.getResourceableId());
diff --git a/src/main/java/org/olat/course/editor/QuickPublishController.java b/src/main/java/org/olat/course/editor/QuickPublishController.java
new file mode 100644
index 00000000000..d071643624e
--- /dev/null
+++ b/src/main/java/org/olat/course/editor/QuickPublishController.java
@@ -0,0 +1,124 @@
+/**
+ * <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.course.editor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+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.tree.TreeNode;
+import org.olat.core.gui.components.velocity.VelocityContainer;
+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.id.OLATResourceable;
+import org.olat.core.util.nodes.INode;
+import org.olat.core.util.resource.OresHelper;
+import org.olat.course.CourseFactory;
+import org.olat.course.ICourse;
+import org.olat.course.tree.CourseEditorTreeModel;
+import org.olat.course.tree.PublishTreeModel;
+
+/**
+ * 
+ * Initial date: 02.12.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class QuickPublishController extends BasicController {
+	
+	
+	
+	private final Link noLink, manualLink, autoLink;
+	private OLATResourceable courseOres;
+	
+	public QuickPublishController(UserRequest ureq, WindowControl wControl, OLATResourceable courseOres) {
+		super(ureq, wControl);
+		this.courseOres = OresHelper.clone(courseOres);
+		
+		VelocityContainer mainVC = createVelocityContainer("quick_publish");
+		noLink = LinkFactory.createButton("pbl.quick.no", mainVC, this);
+		manualLink = LinkFactory.createButton("pbl.quick.manual", mainVC, this);
+		autoLink = LinkFactory.createButton("pbl.quick.auto", mainVC, this);
+		autoLink.setCustomEnabledLinkCSS("btn btn-primary");
+		putInitialPanel(mainVC);
+	}
+	
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		if(noLink == source) {
+			fireEvent(ureq, Event.CANCELLED_EVENT);
+		} else if(manualLink == source) {
+			fireEvent(ureq, EditorMainController.MANUAL_PUBLISH);
+		} else if(autoLink == source) {
+			doAutoPublish();
+			fireEvent(ureq, Event.CHANGED_EVENT);
+		}
+	}
+	
+	private void doAutoPublish() {
+		ICourse course = CourseFactory.loadCourse(courseOres);
+		CourseEditorTreeModel cetm = course.getEditorTreeModel();
+		PublishProcess publishProcess = PublishProcess.getInstance(course, cetm, getLocale());
+		PublishTreeModel publishTreeModel = publishProcess.getPublishTreeModel();
+ 
+		if (publishTreeModel.hasPublishableChanges()) {
+			List<String>nodeToPublish = new ArrayList<String>();
+			visitPublishModel(publishTreeModel.getRootNode(), publishTreeModel, nodeToPublish);
+
+			publishProcess.createPublishSetFor(nodeToPublish);
+			PublishSetInformations set = publishProcess.testPublishSet(getLocale());
+			StatusDescription[] status = set.getWarnings();
+			//publish not possible when there are errors
+			for(int i = 0; i < status.length; i++) {
+				if(status[i].isError()) {
+					logError("Status error by publish: " + status[i].getLongDescription(getLocale()), null);
+					return;
+				}
+			}
+			
+			try {
+				publishProcess.applyPublishSet(getIdentity(), getLocale());
+			} catch(Exception e) {
+				logError("",  e);
+			}
+		}
+	}
+	
+	private static void visitPublishModel(TreeNode node, PublishTreeModel publishTreeModel, Collection<String> nodeToPublish) {
+		int numOfChildren = node.getChildCount();
+		for (int i = 0; i < numOfChildren; i++) {
+			INode child = node.getChildAt(i);
+			if (child instanceof TreeNode && publishTreeModel.isVisible(child)) {
+				nodeToPublish.add(child.getIdent());
+				visitPublishModel((TreeNode)child, publishTreeModel, nodeToPublish);
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/editor/_content/quick_publish.html b/src/main/java/org/olat/course/editor/_content/quick_publish.html
new file mode 100644
index 00000000000..00390742260
--- /dev/null
+++ b/src/main/java/org/olat/course/editor/_content/quick_publish.html
@@ -0,0 +1,6 @@
+<div class="o_info">$r.translate("pbl.quick.description")</div>
+<div class="o_button_group"> 
+	$r.render("pbl.quick.no")
+	$r.render("pbl.quick.manual")
+	$r.render("pbl.quick.auto")
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/editor/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/editor/_i18n/LocalStrings_de.properties
index d98006ff761..9bdc28e8df1 100644
--- a/src/main/java/org/olat/course/editor/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/course/editor/_i18n/LocalStrings_de.properties
@@ -442,6 +442,11 @@ pbl.error.refs=Ein oder mehrere Kursbausteine referenzieren ung\u00FCltige Lernr
 pbl.intro=Bitte w\u00E4hlen Sie die Kursbausteine aus, welche Sie publizieren m\u00F6chten.
 pbl.intro.warning=Beim Publizieren von gel\u00F6schten Kursbausteinen werden deren untergeordnete Kursbausteine ebenfalls gel\u00F6scht. Analog werden beim Publizieren von neuen Kursbausteinen deren untergeordnete Kursbausteine ebenfalls publiziert.
 pbl.intro.warning.header=Wichtig
+pbl.quick.title=\u00C4nderungen publizieren
+pbl.quick.description=Der Kurs enthählt unveröffentliche \u00C4nderungen. Wollen Sie diese vor dem Schliessen publizieren?
+pbl.quick.no=Nein
+pbl.quick.manual=Ja, manuell
+pbl.quick.auto=Ja, automatisch
 pbl.nothing=Es gibt keine \u00C4nderungen, die publiziert werden k\u00F6nnen. Der Kurs ist auf dem neuesten Stand.
 pbl.nothing.button=Zur\u00FCck zum Editor
 pbl.remind.catalog=Falls Sie den Kurs f\u00FCr registrierte Benutzer oder f\u00FCr G\u00E4ste freischalten, denken Sie bitte daran, ihn im Katalog unter der gew\u00FCnschten Rubrik einzutragen.
diff --git a/src/main/java/org/olat/course/nodes/co/CORunController.java b/src/main/java/org/olat/course/nodes/co/CORunController.java
index a73a24b5a37..b5eecfb52af 100755
--- a/src/main/java/org/olat/course/nodes/co/CORunController.java
+++ b/src/main/java/org/olat/course/nodes/co/CORunController.java
@@ -33,7 +33,6 @@ import java.util.Set;
 import java.util.Stack;
 
 import org.olat.basesecurity.GroupRoles;
-import org.olat.core.CoreSpringFactory;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.control.Controller;
diff --git a/src/main/java/org/olat/course/run/CourseRuntimeController.java b/src/main/java/org/olat/course/run/CourseRuntimeController.java
index 06bc27413e8..a99ebaddafb 100644
--- a/src/main/java/org/olat/course/run/CourseRuntimeController.java
+++ b/src/main/java/org/olat/course/run/CourseRuntimeController.java
@@ -45,9 +45,11 @@ import org.olat.core.gui.components.link.LinkFactory;
 import org.olat.core.gui.components.link.LinkPopupSettings;
 import org.olat.core.gui.components.stack.PopEvent;
 import org.olat.core.gui.components.stack.TooledStackedPanel.Align;
+import org.olat.core.gui.components.stack.VetoPopEvent;
 import org.olat.core.gui.control.ChiefController;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.Event;
+import org.olat.core.gui.control.VetoableCloseController;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.creator.ControllerCreator;
 import org.olat.core.gui.control.generic.dtabs.Activateable2;
@@ -129,11 +131,13 @@ import org.springframework.beans.factory.annotation.Autowired;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class CourseRuntimeController extends RepositoryEntryRuntimeController implements GenericEventListener  {
+public class CourseRuntimeController extends RepositoryEntryRuntimeController implements GenericEventListener, VetoableCloseController  {
 	
 	private static final String JOINED = "joined";
 	private static final String LEFT   = "left";
 	private static final String CMD_START_GROUP_PREFIX = "cmd.group.start.ident.";
+	
+	private Delayed delayedClose;
 	//tools
 	private Link folderLink,
 		assessmentLink, archiverLink,
@@ -594,6 +598,14 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 		}
 	}
 
+	@Override
+	public boolean requestForClose(UserRequest ureq) {
+		if(editorCtrl instanceof VetoableCloseController) {
+			return ((VetoableCloseController) editorCtrl).requestForClose(ureq);
+		}
+		return true;
+	}
+
 	@Override
 	public void event(Event event) {
 		if(event instanceof CourseConfigEvent) {				
@@ -640,9 +652,9 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 		} else if(courseStatisticLink == source) {
 			doCourseStatistics(ureq);
 		} else if(testStatisticLink == source) {
-			doAssessmentStatistics(ureq, "command.openteststatistic", "TestStatistics", testStatisticLink, QTIType.test, QTIType.onyx);
+			doAssessmentTestStatistics(ureq);
 		} else if(surveyStatisticLink == source) {
-			doAssessmentStatistics(ureq, "command.opensurveystatistic", "SurveyStatistics", surveyStatisticLink, QTIType.survey);
+			doAssessmentSurveyStatistics(ureq);
 		} else if(assessmentLink == source) {
 			doAssessmentTool(ureq);
 		} else if(calendarLink == source) {
@@ -659,7 +671,15 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 			BusinessGroupRef ref = (BusinessGroupRef)((Link)source).getUserObject();
 			launchGroup(ureq, ref.getKey());
 		} else if(source == toolbarPanel) {
-			if(event instanceof PopEvent) {
+			if(event == Event.CLOSE_EVENT) {
+				if(requestForClose(ureq)) {
+					super.event(ureq, source, event);
+				} else {
+					delayedClose = Delayed.close;
+				}
+			} else if(event instanceof VetoPopEvent) {
+				delayedClose = Delayed.pop;
+			} else if(event instanceof PopEvent) {
 				PopEvent pop = (PopEvent)event;
 				if(pop.getController() != getRunMainController()) {
 					toolControllerDone(ureq);
@@ -692,6 +712,36 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 				initToolbar();
 				toolControllerDone(ureq);
 			}
+		} if(source == editorCtrl && source instanceof VetoableCloseController) {
+			if(event == Event.DONE_EVENT) {
+				if(delayedClose != null) {
+					switch(delayedClose) {
+						case access: doAccess(ureq); break;
+						case archive: doArchive(ureq); break;
+						case assessmentSurveyStatistics: doAssessmentSurveyStatistics(ureq); break;
+						case assessmentTestStatistics: doAssessmentTestStatistics(ureq); break;
+						case assessmentTool: doAssessmentTool(ureq);
+						case certificates: doCertificatesOptions(ureq); break;
+						case courseAreas: doCourseAreas(ureq); break;
+						case courseFolder: doCourseFolder(ureq); break;
+						case courseStatistics: doCourseStatistics(ureq); break;
+						case databases: doDatabases(ureq); break;
+						case details: doDetails(ureq); break;
+						case editSettings: doEditSettings(ureq); break;
+						case efficiencyStatements: doEfficiencyStatements(ureq); break;
+						case catalog: doCatalog(ureq); break;
+						case layout: doLayout(ureq); break;
+						case members: doMembers(ureq); break;
+						case options: doOptions(ureq); break;
+						case orders: doOrders(ureq); break;
+						case close: doClose(ureq); break;
+						case pop: popToRoot(ureq); cleanUp(); break;
+					}
+					delayedClose = null;
+				} else {
+					fireEvent(ureq, Event.DONE_EVENT);
+				}
+			}
 		}
 		
 		super.event(ureq, source, event);
@@ -777,9 +827,9 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 					try {
 						Activateable2 assessmentCtrl = null;
 						if("TestStatistics".equalsIgnoreCase(type)) {
-							assessmentCtrl = doAssessmentStatistics(ureq, "command.openteststatistic", "TestStatistics", testStatisticLink, QTIType.test, QTIType.onyx);
+							assessmentCtrl = doAssessmentTestStatistics(ureq);
 						} else {
-							assessmentCtrl = doAssessmentStatistics(ureq, "command.opensurveystatistic", "SurveyStatistics", surveyStatisticLink, QTIType.survey);
+							assessmentCtrl = doAssessmentSurveyStatistics(ureq);
 						}
 						
 						List<ContextEntry> subEntries;
@@ -808,9 +858,13 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 
 	@Override
 	protected void doAccess(UserRequest ureq) {
-		if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
-			removeCustomCSS(ureq);
-			super.doAccess(ureq);
+		if(delayedClose == Delayed.access || requestForClose(ureq)) {
+			if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
+				removeCustomCSS(ureq);
+				super.doAccess(ureq);
+			}
+		} else {
+			delayedClose = Delayed.access;
 		}
 	}
 
@@ -837,135 +891,208 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 
 	@Override
 	protected void doEditSettings(UserRequest ureq) {
-		if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
-			removeCustomCSS(ureq);
-			super.doEditSettings(ureq);
+		if(delayedClose == Delayed.editSettings || requestForClose(ureq)) {
+			if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
+				removeCustomCSS(ureq);
+				super.doEditSettings(ureq);
+			}
+		} else {
+			delayedClose = Delayed.editSettings;
 		}
 	}
 
 	@Override
 	protected Activateable2 doMembers(UserRequest ureq) {
-		if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT)) {
-			removeCustomCSS(ureq);
-			OLATResourceable ores = OresHelper.createOLATResourceableInstance("MembersMgmt", 0l);
-			ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
-			WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
-			MembersManagementMainController ctrl = new MembersManagementMainController(ureq, addToHistory(ureq, bwControl), getRepositoryEntry());
-			listenTo(ctrl);
-			membersCtrl = pushController(ureq, translate("command.opensimplegroupmngt"), ctrl);
-			setActiveTool(membersLink);
-			currentToolCtr = membersCtrl;
+		if(delayedClose == Delayed.members || requestForClose(ureq)) {
+			if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT)) {
+				removeCustomCSS(ureq);
+				OLATResourceable ores = OresHelper.createOLATResourceableInstance("MembersMgmt", 0l);
+				ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
+				WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
+				MembersManagementMainController ctrl = new MembersManagementMainController(ureq, addToHistory(ureq, bwControl), getRepositoryEntry());
+				listenTo(ctrl);
+				membersCtrl = pushController(ureq, translate("command.opensimplegroupmngt"), ctrl);
+				setActiveTool(membersLink);
+				currentToolCtr = membersCtrl;
+			}
+		} else {
+			delayedClose = Delayed.members;
 		}
 		return membersCtrl;
 	}
 
 	@Override
 	protected void doCatalog(UserRequest ureq) {
-		if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
-			super.doCatalog(ureq);
+		if(delayedClose == Delayed.catalog || requestForClose(ureq)) {
+			if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
+				super.doCatalog(ureq);
+			}
+		} else {
+			delayedClose = Delayed.catalog;
 		}
 	}
 
 	private void doLayout(UserRequest ureq) {
-		if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
-			removeCustomCSS(ureq);
-			ICourse course = CourseFactory.loadCourse(getRepositoryEntry().getOlatResource());
-			boolean managedLayout = RepositoryEntryManagedFlag.isManaged(getRepositoryEntry(), RepositoryEntryManagedFlag.layout);
-			CourseConfig courseConfig = course.getCourseEnvironment().getCourseConfig().clone();
-			CourseLayoutGeneratorController ctrl = new CourseLayoutGeneratorController(ureq, getWindowControl(), course, courseConfig,
-			  		course.getCourseEnvironment(), !managedLayout);
-			listenTo(ctrl);
-			courseLayoutCtrl = pushController(ureq, translate("command.layout"), ctrl);
-			setActiveTool(layoutLink);
-			currentToolCtr = courseLayoutCtrl;
+		if(delayedClose == Delayed.layout || requestForClose(ureq)) {
+			if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
+				removeCustomCSS(ureq);
+				ICourse course = CourseFactory.loadCourse(getRepositoryEntry().getOlatResource());
+				boolean managedLayout = RepositoryEntryManagedFlag.isManaged(getRepositoryEntry(), RepositoryEntryManagedFlag.layout);
+				CourseConfig courseConfig = course.getCourseEnvironment().getCourseConfig().clone();
+				CourseLayoutGeneratorController ctrl = new CourseLayoutGeneratorController(ureq, getWindowControl(), course, courseConfig,
+				  		course.getCourseEnvironment(), !managedLayout);
+				listenTo(ctrl);
+				courseLayoutCtrl = pushController(ureq, translate("command.layout"), ctrl);
+				setActiveTool(layoutLink);
+				currentToolCtr = courseLayoutCtrl;
+			}
+		} else {
+			delayedClose = Delayed.layout;
 		}
 	}
 	
 	private void doOptions(UserRequest ureq) {
-		if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
-			removeCustomCSS(ureq);
-			ICourse course = CourseFactory.loadCourse(getOlatResourceable());
-			CourseConfig courseConfig = course.getCourseEnvironment().getCourseConfig().clone();
-			CourseOptionsController ctrl = new CourseOptionsController(ureq, getWindowControl(), getRepositoryEntry(), courseConfig, true);
-			optionsToolCtr = pushController(ureq, translate("command.options"), ctrl);
-			setActiveTool(optionsLink);
-			currentToolCtr = optionsToolCtr;
+		if(delayedClose == Delayed.options || requestForClose(ureq)) {
+			if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
+				removeCustomCSS(ureq);
+				ICourse course = CourseFactory.loadCourse(getOlatResourceable());
+				CourseConfig courseConfig = course.getCourseEnvironment().getCourseConfig().clone();
+				CourseOptionsController ctrl = new CourseOptionsController(ureq, getWindowControl(), getRepositoryEntry(), courseConfig, true);
+				optionsToolCtr = pushController(ureq, translate("command.options"), ctrl);
+				setActiveTool(optionsLink);
+				currentToolCtr = optionsToolCtr;
+			}
+		} else {
+			delayedClose = Delayed.options;
 		}
 	}
 	
 	private void doCertificatesOptions(UserRequest ureq) {
-		if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
-			removeCustomCSS(ureq);
-			ICourse course = CourseFactory.loadCourse(getOlatResourceable());
-			CourseConfig courseConfig = course.getCourseEnvironment().getCourseConfig().clone();
-			CertificatesOptionsController ctrl = new CertificatesOptionsController(ureq, getWindowControl(), getRepositoryEntry(), courseConfig, true);
-			certificatesOptionsCtrl = pushController(ureq, translate("command.options"), ctrl);
-			setActiveTool(certificatesOptionsLink);
-			currentToolCtr = certificatesOptionsCtrl;
+		if(delayedClose == Delayed.certificates || requestForClose(ureq)) {
+			if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)) {
+				removeCustomCSS(ureq);
+				ICourse course = CourseFactory.loadCourse(getOlatResourceable());
+				CourseConfig courseConfig = course.getCourseEnvironment().getCourseConfig().clone();
+				CertificatesOptionsController ctrl = new CertificatesOptionsController(ureq, getWindowControl(), getRepositoryEntry(), courseConfig, true);
+				certificatesOptionsCtrl = pushController(ureq, translate("command.options"), ctrl);
+				setActiveTool(certificatesOptionsLink);
+				currentToolCtr = certificatesOptionsCtrl;
+			}
+		} else {
+			delayedClose = Delayed.certificates;
 		}
 	}
 	
 	private void doArchive(UserRequest ureq) {
-		if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_ARCHIVING)) {
-			removeCustomCSS(ureq);
-			ICourse course = CourseFactory.loadCourse(getOlatResourceable());
-			ArchiverMainController ctrl = new ArchiverMainController(ureq, getWindowControl(), course, new FullAccessArchiverCallback());
-			listenTo(ctrl);
-			archiverCtrl = pushController(ureq, translate("command.openarchiver"), ctrl);
-			currentToolCtr = archiverCtrl;
-			setActiveTool(archiverLink);
+		if(delayedClose == Delayed.archive || requestForClose(ureq)) {
+			if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_ARCHIVING)) {
+				removeCustomCSS(ureq);
+				ICourse course = CourseFactory.loadCourse(getOlatResourceable());
+				ArchiverMainController ctrl = new ArchiverMainController(ureq, getWindowControl(), course, new FullAccessArchiverCallback());
+				listenTo(ctrl);
+				archiverCtrl = pushController(ureq, translate("command.openarchiver"), ctrl);
+				currentToolCtr = archiverCtrl;
+				setActiveTool(archiverLink);
+			}
+		} else {
+			delayedClose = Delayed.archive;
 		}
 	}
 	
 	private FolderRunController doCourseFolder(UserRequest ureq) {
-		removeCustomCSS(ureq);
-		// Folder for course with custom link model to jump to course nodes
-		ICourse course = CourseFactory.loadCourse(getOlatResourceable());
-		VFSContainer namedCourseFolder = new NamedContainerImpl(translate("command.coursefolder"), course.getCourseFolderContainer());
-		CustomLinkTreeModel customLinkTreeModel = new CourseInternalLinkTreeModel(course.getEditorTreeModel());
-		
-		FolderRunController ctrl = new FolderRunController(namedCourseFolder, true, true, true, ureq, getWindowControl(), null, customLinkTreeModel);
-		ctrl.addLoggingResourceable(LoggingResourceable.wrap(course));
-		courseFolderCtrl = pushController(ureq, translate("command.coursefolder"), ctrl);
-		setActiveTool(folderLink);
-		currentToolCtr = courseFolderCtrl;
+		if(delayedClose == Delayed.courseFolder || requestForClose(ureq)) {
+			removeCustomCSS(ureq);
+			// Folder for course with custom link model to jump to course nodes
+			ICourse course = CourseFactory.loadCourse(getOlatResourceable());
+			VFSContainer namedCourseFolder = new NamedContainerImpl(translate("command.coursefolder"), course.getCourseFolderContainer());
+			CustomLinkTreeModel customLinkTreeModel = new CourseInternalLinkTreeModel(course.getEditorTreeModel());
+			
+			FolderRunController ctrl = new FolderRunController(namedCourseFolder, true, true, true, ureq, getWindowControl(), null, customLinkTreeModel);
+			ctrl.addLoggingResourceable(LoggingResourceable.wrap(course));
+			courseFolderCtrl = pushController(ureq, translate("command.coursefolder"), ctrl);
+			setActiveTool(folderLink);
+			currentToolCtr = courseFolderCtrl;
+		} else {
+			delayedClose = Delayed.courseFolder;
+		}
 		return courseFolderCtrl;
 	}
 	
 	private void doCourseAreas(UserRequest ureq) {
-		removeCustomCSS(ureq);
-		ICourse course = CourseFactory.loadCourse(getOlatResourceable());
-		CourseAreasController ctrl = new CourseAreasController(ureq, getWindowControl(), getRepositoryEntry().getOlatResource());
-		ctrl.addLoggingResourceable(LoggingResourceable.wrap(course));
-		areasCtrl = pushController(ureq, translate("command.courseareas"), ctrl);
-		setActiveTool(areaLink);
-		currentToolCtr = areasCtrl;
+		if(delayedClose == Delayed.courseAreas || requestForClose(ureq)) {
+			removeCustomCSS(ureq);
+			ICourse course = CourseFactory.loadCourse(getOlatResourceable());
+			CourseAreasController ctrl = new CourseAreasController(ureq, getWindowControl(), getRepositoryEntry().getOlatResource());
+			ctrl.addLoggingResourceable(LoggingResourceable.wrap(course));
+			areasCtrl = pushController(ureq, translate("command.courseareas"), ctrl);
+			setActiveTool(areaLink);
+			currentToolCtr = areasCtrl;
+		} else {
+			delayedClose = Delayed.courseAreas;
+		}
 	}
 	
 	private void doDatabases(UserRequest ureq) {
-		if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_DB)) {
-			removeCustomCSS(ureq);
-			ICourse course = CourseFactory.loadCourse(getOlatResourceable());
-			CustomDBMainController ctrl = new CustomDBMainController(ureq, getWindowControl(), course);
-			listenTo(ctrl);
-			databasesCtrl = pushController(ureq, translate("command.opendb"), ctrl);
-			setActiveTool(dbLink);
-			currentToolCtr = databasesCtrl;
+		if(delayedClose == Delayed.databases || requestForClose(ureq)) {
+			if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_DB)) {
+				removeCustomCSS(ureq);
+				ICourse course = CourseFactory.loadCourse(getOlatResourceable());
+				CustomDBMainController ctrl = new CustomDBMainController(ureq, getWindowControl(), course);
+				listenTo(ctrl);
+				databasesCtrl = pushController(ureq, translate("command.opendb"), ctrl);
+				setActiveTool(dbLink);
+				currentToolCtr = databasesCtrl;
+			}
+		} else {
+			delayedClose = Delayed.databases;
 		}
 	}
 	
 	private void doCourseStatistics(UserRequest ureq) {
-		if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_STATISTICS)) {
-			removeCustomCSS(ureq);
-			ICourse course = CourseFactory.loadCourse(getOlatResourceable());
-			StatisticMainController ctrl = new StatisticMainController(ureq, getWindowControl(), course);
-			listenTo(ctrl);
-			statisticsCtrl = pushController(ureq, translate("command.openstatistic"), ctrl);
-			setActiveTool(courseStatisticLink);
-			currentToolCtr = statisticsCtrl;
+		if(delayedClose == Delayed.courseStatistics || requestForClose(ureq)) {
+			if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_STATISTICS)) {
+				removeCustomCSS(ureq);
+				ICourse course = CourseFactory.loadCourse(getOlatResourceable());
+				StatisticMainController ctrl = new StatisticMainController(ureq, getWindowControl(), course);
+				listenTo(ctrl);
+				statisticsCtrl = pushController(ureq, translate("command.openstatistic"), ctrl);
+				setActiveTool(courseStatisticLink);
+				currentToolCtr = statisticsCtrl;
+			}
+		} else {
+			delayedClose = Delayed.courseStatistics;
 		}
 	}
 	
+	private Activateable2 doAssessmentTestStatistics(UserRequest ureq) {
+		Activateable2 controller = null;
+		if(delayedClose == Delayed.assessmentTestStatistics || requestForClose(ureq)) {
+			controller = doAssessmentStatistics(ureq, "command.openteststatistic", "TestStatistics", testStatisticLink, QTIType.test, QTIType.onyx);
+		} else {
+			delayedClose = Delayed.assessmentTestStatistics;
+		}
+		return controller;
+	}
+	
+	private Activateable2 doAssessmentSurveyStatistics(UserRequest ureq) {
+		Activateable2 controller = null;
+		if(delayedClose == Delayed.assessmentSurveyStatistics || requestForClose(ureq)) {
+			controller = doAssessmentStatistics(ureq, "command.opensurveystatistic", "SurveyStatistics", surveyStatisticLink, QTIType.survey);
+		} else {
+			delayedClose = Delayed.assessmentSurveyStatistics;
+		}
+		return controller;
+	}
+	
+	/**
+	 * Only an helper method for the 2 methods above. Don't call it directly, there is no request on close guard.
+	 * @param ureq
+	 * @param i18nCrumbKey
+	 * @param typeName
+	 * @param tool
+	 * @param types
+	 * @return
+	 */
 	private Activateable2 doAssessmentStatistics(UserRequest ureq, String i18nCrumbKey, String typeName, Link tool, QTIType... types) {
 		OLATResourceable ores = OresHelper.createOLATResourceableType(typeName);
 		ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
@@ -984,44 +1111,52 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 	}
 	
 	private Activateable2 doAssessmentTool(UserRequest ureq) {
-		OLATResourceable ores = OresHelper.createOLATResourceableType("assessmentTool");
-		ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
-		WindowControl swControl = addToHistory(ureq, ores, null);
-		
-		// 1) course admins and users with tool right: full access
-		if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) {
-			removeCustomCSS(ureq);
-			AssessmentMainController ctrl = new AssessmentMainController(ureq, swControl, toolbarPanel,
-					getOlatResourceable(), new FullAccessAssessmentCallback(reSecurity.isEntryAdmin()));
-			ctrl.activate(ureq, null, null);
-			listenTo(ctrl);
-			assessmentToolCtr = pushController(ureq, translate("command.openassessment"), ctrl);
-			currentToolCtr = assessmentToolCtr;
-			setActiveTool(assessmentLink);
-			return assessmentToolCtr;
-		}
-		// 2) users with coach right: limited access to coached groups
-		if (reSecurity.isCourseCoach() || reSecurity.isGroupCoach()) {
-			removeCustomCSS(ureq);
-			AssessmentMainController ctrl = new AssessmentMainController(ureq, swControl, toolbarPanel,
-					getOlatResourceable(), new CoachingGroupAccessAssessmentCallback());
-			ctrl.activate(ureq, null, null);
-			listenTo(ctrl);
-			assessmentToolCtr = pushController(ureq, translate("command.openassessment"), ctrl);
-			currentToolCtr = assessmentToolCtr;
-			setActiveTool(assessmentLink);
-			return assessmentToolCtr;
+		if(delayedClose == Delayed.assessmentTool || requestForClose(ureq)) {
+			OLATResourceable ores = OresHelper.createOLATResourceableType("assessmentTool");
+			ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
+			WindowControl swControl = addToHistory(ureq, ores, null);
+			
+			// 1) course admins and users with tool right: full access
+			if (reSecurity.isEntryAdmin() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) {
+				removeCustomCSS(ureq);
+				AssessmentMainController ctrl = new AssessmentMainController(ureq, swControl, toolbarPanel,
+						getOlatResourceable(), new FullAccessAssessmentCallback(reSecurity.isEntryAdmin()));
+				ctrl.activate(ureq, null, null);
+				listenTo(ctrl);
+				assessmentToolCtr = pushController(ureq, translate("command.openassessment"), ctrl);
+				currentToolCtr = assessmentToolCtr;
+				setActiveTool(assessmentLink);
+				return assessmentToolCtr;
+			}
+			// 2) users with coach right: limited access to coached groups
+			if (reSecurity.isCourseCoach() || reSecurity.isGroupCoach()) {
+				removeCustomCSS(ureq);
+				AssessmentMainController ctrl = new AssessmentMainController(ureq, swControl, toolbarPanel,
+						getOlatResourceable(), new CoachingGroupAccessAssessmentCallback());
+				ctrl.activate(ureq, null, null);
+				listenTo(ctrl);
+				assessmentToolCtr = pushController(ureq, translate("command.openassessment"), ctrl);
+				currentToolCtr = assessmentToolCtr;
+				setActiveTool(assessmentLink);
+				return assessmentToolCtr;
+			}
+		} else {
+			delayedClose = Delayed.assessmentTool;
 		}
 		return null;
 	}
 	
 	private void doEfficiencyStatements(UserRequest ureq) {
-		// will not be disposed on course run dispose, popus up as new browserwindow
-		CertificateAndEfficiencyStatementController efficiencyStatementController = new CertificateAndEfficiencyStatementController(getWindowControl(), ureq, getRepositoryEntry());
-		listenTo(efficiencyStatementController);
-		efficiencyStatementController = pushController(ureq, translate("command.efficiencystatement"), efficiencyStatementController);
-		currentToolCtr = efficiencyStatementController;
-		setActiveTool(efficiencyStatementsLink);
+		if(delayedClose == Delayed.efficiencyStatements || requestForClose(ureq)) {
+			// will not be disposed on course run dispose, popus up as new browserwindow
+			CertificateAndEfficiencyStatementController efficiencyStatementController = new CertificateAndEfficiencyStatementController(getWindowControl(), ureq, getRepositoryEntry());
+			listenTo(efficiencyStatementController);
+			efficiencyStatementController = pushController(ureq, translate("command.efficiencystatement"), efficiencyStatementController);
+			currentToolCtr = efficiencyStatementController;
+			setActiveTool(efficiencyStatementsLink);
+		} else {
+			delayedClose = Delayed.efficiencyStatements;
+		}
 	}
 	
 	private void toggleGlossary(UserRequest ureq) {
@@ -1292,4 +1427,27 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 			run.doDisposeAfterEvent();
 		}
 	}
+	
+	private enum Delayed {
+		access,
+		archive,
+		assessmentSurveyStatistics,
+		assessmentTestStatistics,
+		assessmentTool,
+		certificates,
+		courseAreas,
+		courseFolder,
+		courseStatistics,
+		databases,
+		details,
+		editSettings,
+		efficiencyStatements,
+		catalog,
+		layout,
+		members,
+		options,
+		orders,
+		close,
+		pop
+	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/QTIRuntimeController.java b/src/main/java/org/olat/ims/qti/QTIRuntimeController.java
index 93b7ecd0984..2aac2a228d9 100644
--- a/src/main/java/org/olat/ims/qti/QTIRuntimeController.java
+++ b/src/main/java/org/olat/ims/qti/QTIRuntimeController.java
@@ -52,9 +52,9 @@ public class QTIRuntimeController extends RepositoryEntryRuntimeController imple
 	 * This is only used by the QTI editor
 	 */
 	@Override
-	public boolean requestForClose() {
+	public boolean requestForClose(UserRequest ureq) {
 		if(editorCtrl instanceof VetoableCloseController) {
-			return ((VetoableCloseController)editorCtrl).requestForClose();
+			return ((VetoableCloseController)editorCtrl).requestForClose(ureq);
 		}
 		return true;
 	}
@@ -87,13 +87,13 @@ public class QTIRuntimeController extends RepositoryEntryRuntimeController imple
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
 		if(event == Event.CLOSE_EVENT) {
-			if(requestForClose()) {
+			if(requestForClose(ureq)) {
 				super.event(ureq, source, event);
 			} else {
 				delayedClose = Delayed.close;
 			}
 		} else if(event instanceof VetoPopEvent) {
-			if(requestForClose()) {
+			if(requestForClose(ureq)) {
 				super.event(ureq, source, event);
 			} else {
 				delayedClose = Delayed.pop;
@@ -105,7 +105,7 @@ public class QTIRuntimeController extends RepositoryEntryRuntimeController imple
 
 	@Override
 	protected void doAccess(UserRequest ureq) {
-		if(requestForClose()) {
+		if(requestForClose(ureq)) {
 			super.doAccess(ureq);
 		} else {
 			delayedClose = Delayed.access; 
@@ -114,7 +114,7 @@ public class QTIRuntimeController extends RepositoryEntryRuntimeController imple
 
 	@Override
 	protected void doDetails(UserRequest ureq) {
-		if(requestForClose()) {
+		if(requestForClose(ureq)) {
 			super.doDetails(ureq);
 		} else {
 			delayedClose = Delayed.details; 
@@ -123,7 +123,7 @@ public class QTIRuntimeController extends RepositoryEntryRuntimeController imple
 
 	@Override
 	protected void doEditSettings(UserRequest ureq) {
-		if(requestForClose()) {
+		if(requestForClose(ureq)) {
 			super.doEditSettings(ureq);
 		} else {
 			delayedClose = Delayed.editSettings; 
@@ -132,7 +132,7 @@ public class QTIRuntimeController extends RepositoryEntryRuntimeController imple
 
 	@Override
 	protected void doCatalog(UserRequest ureq) {
-		if(requestForClose()) {
+		if(requestForClose(ureq)) {
 			super.doCatalog(ureq);
 		} else {
 			delayedClose = Delayed.catalog; 
@@ -141,7 +141,7 @@ public class QTIRuntimeController extends RepositoryEntryRuntimeController imple
 
 	@Override
 	protected Activateable2 doMembers(UserRequest ureq) {
-		if(requestForClose()) {
+		if(requestForClose(ureq)) {
 			return super.doMembers(ureq);
 		} else {
 			delayedClose = Delayed.members;
@@ -151,7 +151,7 @@ public class QTIRuntimeController extends RepositoryEntryRuntimeController imple
 
 	@Override
 	protected void doOrders(UserRequest ureq) {
-		if(requestForClose()) {
+		if(requestForClose(ureq)) {
 			super.doOrders(ureq);
 		} else {
 			delayedClose = Delayed.orders; 
diff --git a/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java b/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java
index 19610fc0933..aabfa6a074c 100644
--- a/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java
+++ b/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java
@@ -1156,10 +1156,8 @@ public class QTIEditorMainController extends MainLayoutBasicController implement
 		stackedPanel.addTool(closeLink, Align.right);
 	}
 
-	/**
-	 * @see org.olat.core.gui.control.VetoableCloseController#requestForClose()
-	 */
-	public boolean requestForClose() {		
+	@Override
+	public boolean requestForClose(UserRequest ureq) {		
 		// enter save/discard dialog if not already in it
 		if (cmcExit == null) {
 			exitVC = createVelocityContainer("exitDialog");
diff --git a/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java b/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java
index da3120e9810..1920734d723 100644
--- a/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java
+++ b/src/main/java/org/olat/repository/ui/RepositoryEntryRuntimeController.java
@@ -399,17 +399,17 @@ public class RepositoryEntryRuntimeController extends MainLayoutBasicController
 		entries = removeRepositoryEntry(entries);
 		if(entries != null && entries.size() > 0) {
 			String type = entries.get(0).getOLATResourceable().getResourceableTypeName();
-			if("Editor".equals(type)) {
+			if("Editor".equalsIgnoreCase(type)) {
 				if(handler.supportsEdit(re) == EditionSupport.yes) {
 					doEdit(ureq);
 				}
-			} else if("Catalog".equals(type)) {
+			} else if("Catalog".equalsIgnoreCase(type)) {
 				doCatalog(ureq);
-			} else if("Infos".equals(type)) {
+			} else if("Infos".equalsIgnoreCase(type)) {
 				doDetails(ureq);	
-			} else if("EditDescription".equals(type)) {
+			} else if("EditDescription".equalsIgnoreCase(type)) {
 				doEditSettings(ureq);
-			} else if("MembersMgmt".equals(type)) {
+			} else if("MembersMgmt".equalsIgnoreCase(type)) {
 				doMembers(ureq);
 			}
 		}
diff --git a/src/main/java/org/olat/repository/ui/WebDocumentRunController.java b/src/main/java/org/olat/repository/ui/WebDocumentRunController.java
index 50e12c42acd..8d722596728 100644
--- a/src/main/java/org/olat/repository/ui/WebDocumentRunController.java
+++ b/src/main/java/org/olat/repository/ui/WebDocumentRunController.java
@@ -1,3 +1,22 @@
+/**
+ * <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.repository.ui;
 
 import javax.servlet.http.HttpServletRequest;
-- 
GitLab