From 3e4fbfba4de6aefa38d9de6eacd47fc2474681a5 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Fri, 30 Oct 2015 17:21:29 +0100
Subject: [PATCH] OO-1593: navigate around course element with next, previous
 and menu

---
 .../course/assessment/AssessmentHelper.java   | 57 +++++++++++++
 .../IdentityAssessmentOverviewController.java | 14 +++-
 .../AssessedIdentityLargeInfosController.java | 66 +++++++++++++++
 .../AssessmentCourseOverviewController.java   | 25 +++++-
 ...ssmentCourseStatisticsSmallController.java | 13 +--
 ...essmentIdentitiesCourseTreeController.java | 68 +++-------------
 .../AssessmentIdentityCourseController.java   | 79 +++++++++++++++---
 ...ssessmentIdentityCourseNodeController.java | 14 ++--
 .../ui/tool/AssessmentToolController.java     | 21 ++++-
 .../tool/CourseNodeSelectionController.java   | 81 +++++++++++++++++++
 .../ui/tool/_content/course_node_chooser.html |  1 +
 .../ui/tool/_content/course_overview.html     | 12 ++-
 .../identity_personal_node_infos.html         |  5 +-
 .../ui/tool/_content/user_infos_large.html    |  4 +
 .../ui/tool/_i18n/LocalStrings_de.properties  |  2 +
 .../ui/tool/event/CourseNodeEvent.java        | 46 +++++++++++
 16 files changed, 421 insertions(+), 87 deletions(-)
 create mode 100644 src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityLargeInfosController.java
 create mode 100644 src/main/java/org/olat/course/assessment/ui/tool/CourseNodeSelectionController.java
 create mode 100644 src/main/java/org/olat/course/assessment/ui/tool/_content/course_node_chooser.html
 create mode 100644 src/main/java/org/olat/course/assessment/ui/tool/_content/user_infos_large.html
 create mode 100644 src/main/java/org/olat/course/assessment/ui/tool/event/CourseNodeEvent.java

diff --git a/src/main/java/org/olat/course/assessment/AssessmentHelper.java b/src/main/java/org/olat/course/assessment/AssessmentHelper.java
index 3bba13632ac..6b2c4b37b8d 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentHelper.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentHelper.java
@@ -35,6 +35,9 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
+import org.olat.core.gui.components.tree.GenericTreeModel;
+import org.olat.core.gui.components.tree.GenericTreeNode;
+import org.olat.core.gui.components.tree.TreeModel;
 import org.olat.core.id.Identity;
 import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.logging.OLog;
@@ -47,6 +50,7 @@ import org.olat.course.ICourse;
 import org.olat.course.assessment.model.AssessmentNodeData;
 import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.CourseNode;
+import org.olat.course.nodes.CourseNodeFactory;
 import org.olat.course.nodes.ProjectBrokerCourseNode;
 import org.olat.course.nodes.STCourseNode;
 import org.olat.course.nodes.ScormCourseNode;
@@ -308,6 +312,59 @@ public class AssessmentHelper {
 		}
 	}
 	
+	/**
+	 * 
+	 * @param course
+	 * @return
+	 */
+	public static TreeModel assessmentTreeModel(ICourse course) {
+		CourseNode rootNode = course.getRunStructure().getRootNode();
+		GenericTreeModel gtm = new GenericTreeModel();
+		GenericTreeNode node = new GenericTreeNode();
+		node.setTitle(rootNode.getShortTitle());
+		node.setUserObject(rootNode);
+		node.setIconCssClass(CourseNodeFactory.getInstance().getCourseNodeConfiguration(rootNode.getType()).getIconCSSClass());
+		gtm.setRootNode(node);
+		
+		List<GenericTreeNode> children = addAssessableNodesToList(rootNode);
+		children.forEach((child) -> node.addChild(child));
+		return gtm;
+	}
+	
+	private static List<GenericTreeNode> addAssessableNodesToList(CourseNode parentCourseNode) {
+		List<GenericTreeNode> result = new ArrayList<>();
+		for(int i=0; i<parentCourseNode.getChildCount(); i++) {
+			CourseNode courseNode = (CourseNode)parentCourseNode.getChildAt(i);
+			List<GenericTreeNode> assessableChildren = addAssessableNodesToList(courseNode);
+			
+			if (assessableChildren.size() > 0 || isAssessable(courseNode)) {
+				GenericTreeNode node = new GenericTreeNode();
+				node.setTitle(courseNode.getShortTitle());
+				node.setUserObject(courseNode);
+				node.setIconCssClass(CourseNodeFactory.getInstance().getCourseNodeConfiguration(courseNode.getType()).getIconCSSClass());
+				result.add(node);
+				assessableChildren.forEach((child) -> node.addChild(child));
+			}
+		}
+		return result;
+	}
+	
+	private static boolean isAssessable(CourseNode courseNode) {
+		boolean assessable = false;
+		if (courseNode instanceof AssessableCourseNode && !(courseNode instanceof ProjectBrokerCourseNode)) {
+			AssessableCourseNode assessableCourseNode = (AssessableCourseNode) courseNode;
+			if (assessableCourseNode.hasDetails()
+				|| assessableCourseNode.hasAttemptsConfigured()
+				|| assessableCourseNode.hasScoreConfigured()
+				|| assessableCourseNode.hasPassedConfigured()
+				|| assessableCourseNode.hasCommentConfigured()) {
+
+				assessable = true;
+			}
+		}
+		return assessable;
+	}
+	
 
 	public static List<Map<String,Object>> assessmentNodeDataListToMap(List<AssessmentNodeData> assessmentNodeData) {
 		List<Map<String,Object>> maps = new ArrayList<>(assessmentNodeData.size());
diff --git a/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java b/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java
index 72fed0ed2f7..d87f9cba9b0 100644
--- a/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java
+++ b/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java
@@ -140,6 +140,18 @@ public class IdentityAssessmentOverviewController extends BasicController {
 		putInitialPanel(main);
 	}
 	
+	public boolean isRoot(CourseNode node) {
+		return node != null && node.getIdent().equals(runStructure.getRootNode().getIdent());
+	}
+	
+	public int getNumberOfNodes() {
+		return nodesTableModel.getRowCount();
+	}
+	
+	public CourseNode getNodeByIdent(String ident) {
+		return runStructure.getNode(ident);
+	}
+	
 	public CourseNode getNextNode(CourseNode node) {
 		int index = getIndexOf(node);
 		
@@ -178,7 +190,7 @@ public class IdentityAssessmentOverviewController extends BasicController {
 		return null;
 	}
 	
-	private int getIndexOf(CourseNode node) {
+	public int getIndexOf(CourseNode node) {
 		for(int i=nodesTableModel.getRowCount(); i-->0; ) {
 			Object rowIdentityKey = nodesTableModel.getObject(i).getIdent();
 			if(rowIdentityKey.equals(node.getIdent())) {
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityLargeInfosController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityLargeInfosController.java
new file mode 100644
index 00000000000..78e7f306a13
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityLargeInfosController.java
@@ -0,0 +1,66 @@
+/**
+ * <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.assessment.ui.tool;
+
+import org.olat.admin.user.UserShortDescription;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+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.Identity;
+import org.olat.user.DisplayPortraitController;
+
+/**
+ * 
+ * Initial date: 28.10.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AssessedIdentityLargeInfosController extends BasicController {
+
+	private final VelocityContainer mainVC;
+	private final DisplayPortraitController portraitCtr;
+	private final UserShortDescription userShortDescrCtr;
+	
+	public AssessedIdentityLargeInfosController(UserRequest ureq, WindowControl wControl, Identity assessedIdentity) {
+		super(ureq, wControl);
+		mainVC = createVelocityContainer("user_infos_large");
+		mainVC.contextPut("user", assessedIdentity.getUser());
+
+		portraitCtr = new DisplayPortraitController(ureq, getWindowControl(), assessedIdentity, true, true);
+		mainVC.put("portrait", portraitCtr.getInitialComponent());
+		userShortDescrCtr = new UserShortDescription(ureq, getWindowControl(), assessedIdentity);
+		mainVC.put("userShortDescription", userShortDescrCtr.getInitialComponent());
+		
+		putInitialPanel(mainVC);	
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		//
+	}
+}
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 31f93a4ebaa..15df0e978cc 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
@@ -19,12 +19,19 @@
  */
 package org.olat.course.assessment.ui.tool;
 
+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.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.gui.control.generic.dtabs.Activateable2;
+import org.olat.core.id.context.ContextEntry;
+import org.olat.core.id.context.StateEntry;
 import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback;
 import org.olat.repository.RepositoryEntry;
 
@@ -34,12 +41,15 @@ import org.olat.repository.RepositoryEntry;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class AssessmentCourseOverviewController extends BasicController {
+public class AssessmentCourseOverviewController extends BasicController implements Activateable2 {
+	
+	protected static final Event SELECT_USERS_EVENT = new Event("assessment-tool-select-users");
 	
 	private final VelocityContainer mainVC;
 	private final AssessmentToReviewSmallController toReviewCtrl;
 	private final AssessmentCourseStatisticsSmallController statisticsCtrl;
 
+	private Link assessedIdentitiesLink;
 	
 	public AssessmentCourseOverviewController(UserRequest ureq, WindowControl wControl,
 			RepositoryEntry courseEntry, AssessmentToolSecurityCallback assessmentCallback) {
@@ -54,6 +64,10 @@ public class AssessmentCourseOverviewController extends BasicController {
 		statisticsCtrl = new AssessmentCourseStatisticsSmallController(ureq, getWindowControl(), courseEntry, assessmentCallback);
 		listenTo(statisticsCtrl);
 		mainVC.put("statistics", statisticsCtrl.getInitialComponent());
+		
+		int numOfAssessedIdentities = statisticsCtrl.getNumOfAssessedIdentities();
+		assessedIdentitiesLink = LinkFactory.createLink("assessed.identities", "assessed.identities", getTranslator(), mainVC, this, Link.NONTRANSLATED);
+		assessedIdentitiesLink.setCustomDisplayText(translate("assessment.tool.numOfAssessedIdentities", new String[]{ Integer.toString(numOfAssessedIdentities) }));
 
 		putInitialPanel(mainVC);
 	}
@@ -64,9 +78,14 @@ public class AssessmentCourseOverviewController extends BasicController {
 	}
 
 	@Override
-	protected void event(UserRequest ureq, Component source, Event event) {
+	public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
 		//
 	}
 
-
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		if(assessedIdentitiesLink == source) {
+			fireEvent(ureq, SELECT_USERS_EVENT);
+		}
+	}
 }
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseStatisticsSmallController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseStatisticsSmallController.java
index 8616e2c5309..3ca65b82a46 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseStatisticsSmallController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseStatisticsSmallController.java
@@ -46,6 +46,8 @@ public class AssessmentCourseStatisticsSmallController extends BasicController {
 	private final RepositoryEntry courseEntry;
 	private final AssessmentToolSecurityCallback assessmentCallback;
 	
+	private int numOfAssessedIdentities;
+	
 	@Autowired
 	private AssessmentToolManager assessmentToolManager;
 	
@@ -60,11 +62,16 @@ public class AssessmentCourseStatisticsSmallController extends BasicController {
 		updateStatistics();
 	}
 	
+	public int getNumOfAssessedIdentities() {
+		return numOfAssessedIdentities;
+	}
+	
 	public void updateStatistics() {
 		SearchAssessedIdentityParams params = new SearchAssessedIdentityParams(courseEntry, null, null, assessmentCallback);
 		CourseStatistics stats = assessmentToolManager.getStatistics(getIdentity(), params);
 		
-		mainVC.contextPut("numOfAssessedIdentities", stats.getNumOfAssessedIdentities());
+		numOfAssessedIdentities = stats.getNumOfAssessedIdentities();
+		mainVC.contextPut("numOfAssessedIdentities", numOfAssessedIdentities);
 		mainVC.contextPut("scoreAverage", AssessmentHelper.getRoundedScore(stats.getAverageScore()));
 		mainVC.contextPut("numOfPassed", stats.getCountPassed());
 		int percentPassed = Math.round(100.0f * ((float)stats.getCountPassed() / (float)stats.getNumOfAssessedIdentities()));
@@ -74,8 +81,6 @@ public class AssessmentCourseStatisticsSmallController extends BasicController {
 		mainVC.contextPut("percentFailed", percentFailed);
 		
 		mainVC.contextPut("numOfInitialLaunch", stats.getInitialLaunch());
-		
-		
 	}
 
 	@Override
@@ -87,6 +92,4 @@ public class AssessmentCourseStatisticsSmallController extends BasicController {
 	protected void event(UserRequest ureq, Component source, Event event) {
 		//
 	}
-
-
 }
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java
index 42d7a5784f1..39712ccaf26 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java
@@ -19,7 +19,6 @@
  */
 package org.olat.course.assessment.ui.tool;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
@@ -27,8 +26,6 @@ import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.panel.Panel;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
-import org.olat.core.gui.components.tree.GenericTreeModel;
-import org.olat.core.gui.components.tree.GenericTreeNode;
 import org.olat.core.gui.components.tree.MenuTree;
 import org.olat.core.gui.components.tree.TreeModel;
 import org.olat.core.gui.components.tree.TreeNode;
@@ -37,14 +34,15 @@ 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.OLATResourceable;
+import org.olat.core.id.context.BusinessControlFactory;
 import org.olat.core.id.context.ContextEntry;
 import org.olat.core.id.context.StateEntry;
+import org.olat.core.util.resource.OresHelper;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
-import org.olat.course.nodes.AssessableCourseNode;
+import org.olat.course.assessment.AssessmentHelper;
 import org.olat.course.nodes.CourseNode;
-import org.olat.course.nodes.CourseNodeFactory;
-import org.olat.course.nodes.ProjectBrokerCourseNode;
 import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback;
 import org.olat.repository.RepositoryEntry;
 
@@ -75,7 +73,7 @@ public class AssessmentIdentitiesCourseTreeController extends BasicController im
 		
 		// Navigation menu
 		menuTree = new MenuTree("menuTree");
-		TreeModel tm = buildTreeModel(course);
+		TreeModel tm = AssessmentHelper.assessmentTreeModel(course);
 		menuTree.setTreeModel(tm);
 		menuTree.setSelectedNodeId(tm.getRootNode().getIdent());
 		menuTree.addListener(this);
@@ -88,54 +86,6 @@ public class AssessmentIdentitiesCourseTreeController extends BasicController im
 		putInitialPanel(columLayoutCtr.getInitialComponent());
 	}
 	
-	private TreeModel buildTreeModel(ICourse course) {
-		CourseNode rootNode = course.getRunStructure().getRootNode();
-		GenericTreeModel gtm = new GenericTreeModel();
-		GenericTreeNode node = new GenericTreeNode();
-		node.setTitle(rootNode.getShortTitle());
-		node.setUserObject(rootNode);
-		node.setIconCssClass(CourseNodeFactory.getInstance().getCourseNodeConfiguration(rootNode.getType()).getIconCSSClass());
-		gtm.setRootNode(node);
-		
-		List<GenericTreeNode> children = addAssessableNodesToList(rootNode);
-		children.forEach((child) -> node.addChild(child));
-		return gtm;
-	}
-	
-	private List<GenericTreeNode> addAssessableNodesToList(CourseNode parentCourseNode) {
-		List<GenericTreeNode> result = new ArrayList<>();
-		for(int i=0; i<parentCourseNode.getChildCount(); i++) {
-			CourseNode courseNode = (CourseNode)parentCourseNode.getChildAt(i);
-			List<GenericTreeNode> assessableChildren = addAssessableNodesToList(courseNode);
-			
-			if (assessableChildren.size() > 0 || isAssessable(courseNode)) {
-				GenericTreeNode node = new GenericTreeNode();
-				node.setTitle(courseNode.getShortTitle());
-				node.setUserObject(courseNode);
-				node.setIconCssClass(CourseNodeFactory.getInstance().getCourseNodeConfiguration(courseNode.getType()).getIconCSSClass());
-				result.add(node);
-				assessableChildren.forEach((child) -> node.addChild(child));
-			}
-		}
-		return result;
-	}
-	
-	private boolean isAssessable(CourseNode courseNode) {
-		boolean assessable = false;
-		if (courseNode instanceof AssessableCourseNode && !(courseNode instanceof ProjectBrokerCourseNode)) {
-			AssessableCourseNode assessableCourseNode = (AssessableCourseNode) courseNode;
-			if (assessableCourseNode.hasDetails()
-				|| assessableCourseNode.hasAttemptsConfigured()
-				|| assessableCourseNode.hasScoreConfigured()
-				|| assessableCourseNode.hasPassedConfigured()
-				|| assessableCourseNode.hasCommentConfigured()) {
-
-				assessable = true;
-			}
-		}
-		return assessable;
-	}
-	
 	@Override
 	protected void doDispose() {
 		//
@@ -165,14 +115,18 @@ public class AssessmentIdentitiesCourseTreeController extends BasicController im
 
 	private void doSelectCourseNode(UserRequest ureq, CourseNode courseNode) {
 		removeAsListenerAndDispose(currentCtrl);
+		
+		OLATResourceable ores = OresHelper.createOLATResourceableInstance("Node", new Long(courseNode.getIdent()));
+		WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
 
 		ICourse course = CourseFactory.loadCourse(courseEntry);
 		if(course.getRunStructure().getRootNode().equals(courseNode)) {
-			currentCtrl = new AssessmentIdentitiesCourseController(ureq, getWindowControl(), stackPanel, courseEntry, assessmentCallback);
+			currentCtrl = new AssessmentIdentitiesCourseController(ureq, bwControl, stackPanel, courseEntry, assessmentCallback);
 		} else {
-			currentCtrl = new AssessmentIdentitiesCourseNodeController(ureq, getWindowControl(), stackPanel, courseEntry, courseNode, assessmentCallback);
+			currentCtrl = new AssessmentIdentitiesCourseNodeController(ureq, bwControl, stackPanel, courseEntry, courseNode, assessmentCallback);
 		}
 		listenTo(currentCtrl);
 		mainPanel.setContent(currentCtrl.getInitialComponent());
+		addToHistory(ureq, currentCtrl);
 	}
 }
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java
index 422a9b0fd44..3d1ba2f1275 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java
@@ -31,15 +31,17 @@ 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.closablewrapper.CloseableCalloutWindowController;
 import org.olat.core.id.Identity;
 import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.id.Roles;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
-import org.olat.course.assessment.AssessedIdentityInfosController;
 import org.olat.course.assessment.IdentityAssessmentOverviewController;
+import org.olat.course.assessment.ui.tool.event.CourseNodeEvent;
 import org.olat.course.config.CourseConfig;
 import org.olat.course.nodes.CourseNode;
+import org.olat.course.nodes.CourseNodeFactory;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.repository.RepositoryEntry;
@@ -56,14 +58,16 @@ public class AssessmentIdentityCourseController extends BasicController {
 	private final Identity assessedIdentity;
 	private final RepositoryEntry courseEntry;
 
-	private Link nextLink, previousLink;
 	private final TooledStackedPanel stackPanel;
 	private final VelocityContainer identityAssessmentVC;
+	private Link nextLink, previousLink, courseNodeSelectionLink;
 	
 	private IdentityCertificatesController certificateCtrl;
-	private AssessedIdentityInfosController infosController;
+	private AssessedIdentityLargeInfosController infosController;
 	private IdentityAssessmentOverviewController treeOverviewCtrl;
 	private AssessmentIdentityCourseNodeController currentNodeCtrl;
+	private CourseNodeSelectionController courseNodeChooserCtrl;
+	private CloseableCalloutWindowController courseNodeChooserCalloutCtrl;
 	
 	@Autowired
 	private BaseSecurity securityManager;
@@ -79,7 +83,7 @@ public class AssessmentIdentityCourseController extends BasicController {
 		identityAssessmentVC = createVelocityContainer("identity_personal_infos");
 		identityAssessmentVC.contextPut("user", assessedIdentity.getUser());
 		
-		infosController = new AssessedIdentityInfosController(ureq, wControl, assessedIdentity);
+		infosController = new AssessedIdentityLargeInfosController(ureq, wControl, assessedIdentity);
 		listenTo(infosController);
 		identityAssessmentVC.put("identityInfos", infosController.getInitialComponent());
 		
@@ -95,7 +99,7 @@ public class AssessmentIdentityCourseController extends BasicController {
 			listenTo(certificateCtrl);
 		}
 
-		treeOverviewCtrl = new IdentityAssessmentOverviewController(ureq, getWindowControl(), assessedUserCourseEnv, true, true, false);
+		treeOverviewCtrl = new IdentityAssessmentOverviewController(ureq, getWindowControl(), assessedUserCourseEnv, true, false, true);
 		listenTo(treeOverviewCtrl);
 		identityAssessmentVC.put("courseOverview", treeOverviewCtrl.getInitialComponent());
 
@@ -117,6 +121,19 @@ public class AssessmentIdentityCourseController extends BasicController {
 			if(IdentityAssessmentOverviewController.EVENT_NODE_SELECTED.equals(event)) {
 				doSelectCourseNode(ureq, treeOverviewCtrl.getSelectedCourseNode());
 			}
+		} else if(courseNodeChooserCtrl == source) {
+			if(CourseNodeEvent.SELECT_COURSE_NODE.equals(event.getCommand())
+					&& event instanceof CourseNodeEvent) {
+				CourseNodeEvent cne = (CourseNodeEvent)event;
+				CourseNode selectedNode = treeOverviewCtrl.getNodeByIdent(cne.getIdent());
+				if(selectedNode != null) {
+					doSelectCourseNode(ureq, selectedNode);
+				}
+			}
+			courseNodeChooserCalloutCtrl.deactivate();
+			cleanUp();
+		} else if(courseNodeChooserCalloutCtrl == source) {
+			cleanUp();
 		}
 		super.event(ureq, source, event);
 	}
@@ -127,9 +144,31 @@ public class AssessmentIdentityCourseController extends BasicController {
 			doPreviousNode(ureq);
 		} else if(nextLink == source) {
 			doNextNode(ureq);
+		} else if(courseNodeSelectionLink == source) {
+			doSelectCourseNode(ureq);
 		}
 	}
 	
+	private void cleanUp() {
+		removeAsListenerAndDispose(courseNodeChooserCtrl);
+		removeAsListenerAndDispose(courseNodeChooserCalloutCtrl);
+		courseNodeChooserCalloutCtrl = null;
+		courseNodeChooserCtrl = null;
+	}
+	
+	private void doSelectCourseNode(UserRequest ureq) {
+		removeAsListenerAndDispose(courseNodeChooserCtrl);
+		removeAsListenerAndDispose(courseNodeChooserCalloutCtrl);
+		
+		courseNodeChooserCtrl = new CourseNodeSelectionController(ureq, getWindowControl(), courseEntry);
+		listenTo(courseNodeChooserCtrl);
+		
+		courseNodeChooserCalloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(),
+				courseNodeChooserCtrl.getInitialComponent(), courseNodeSelectionLink, "", true, "");
+		listenTo(courseNodeChooserCalloutCtrl);
+		courseNodeChooserCalloutCtrl.activate();
+	}
+	
 	private void doNextNode(UserRequest ureq) {
 		stackPanel.popController(currentNodeCtrl);
 		
@@ -149,7 +188,14 @@ public class AssessmentIdentityCourseController extends BasicController {
 	}
 	
 	private void doSelectCourseNode(UserRequest ureq, CourseNode courseNode) {
-		if(courseNode == null) return;
+		if(courseNode == null) {
+			return;
+		} 
+		stackPanel.popUpToController(this);
+		if(treeOverviewCtrl.isRoot(courseNode)) {
+			return;
+		}
+		
 		removeAsListenerAndDispose(currentNodeCtrl);
 
 		currentNodeCtrl = new AssessmentIdentityCourseNodeController(ureq, getWindowControl(), stackPanel,
@@ -157,11 +203,24 @@ public class AssessmentIdentityCourseController extends BasicController {
 		listenTo(currentNodeCtrl);
 		stackPanel.pushController(courseNode.getShortTitle(), currentNodeCtrl);
 		
-		previousLink = LinkFactory.createToolLink("previouselement","", this, "o_icon_previous_toolbar");
+		int index = treeOverviewCtrl.getIndexOf(courseNode);
+		int numOfNodes = treeOverviewCtrl.getNumberOfNodes();
+		
+		previousLink = LinkFactory.createToolLink("previouselement","", this, "o_icon_previous");
 		previousLink.setTitle(translate("command.previous"));
-		stackPanel.addTool(previousLink, Align.rightEdge, false, "o_tool_previous");
-		nextLink = LinkFactory.createToolLink("nextelement","", this, "o_icon_next_toolbar");
+		previousLink.setEnabled(index > 1 && index <= numOfNodes);
+		stackPanel.addTool(previousLink, Align.rightEdge, false, "");
+
+		courseNodeSelectionLink =  LinkFactory.createToolLink("node.select", "node.select", courseNode.getShortTitle(), this);
+		String courseNodeCssClass = CourseNodeFactory.getInstance()
+				.getCourseNodeConfigurationEvenForDisabledBB(courseNode.getType()).getIconCSSClass();
+		courseNodeSelectionLink.setIconLeftCSS("o_icon " + courseNodeCssClass);
+		courseNodeSelectionLink.setIconRightCSS("o_icon o_icon_caret");
+		stackPanel.addTool(courseNodeSelectionLink, Align.rightEdge, false);
+		
+		nextLink = LinkFactory.createToolLink("nextelement","", this, "o_icon_next");
 		nextLink.setTitle(translate("command.next"));
-		stackPanel.addTool(nextLink, Align.rightEdge, false, "o_tool_next");
+		nextLink.setEnabled(index > 0 && index < numOfNodes);
+		stackPanel.addTool(nextLink, Align.rightEdge, false, "");
 	}
 }
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java
index a902308fbd8..2d98fcd76bd 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java
@@ -34,16 +34,17 @@ import org.olat.core.id.Roles;
 import org.olat.core.util.Formatter;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
-import org.olat.course.assessment.AssessedIdentityInfosController;
 import org.olat.course.assessment.AssessmentForm;
 import org.olat.course.assessment.OpenSubDetailsEvent;
 import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.CourseNode;
+import org.olat.course.nodes.CourseNodeFactory;
 import org.olat.course.nodes.MSCourseNode;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.modules.ModuleConfiguration;
 import org.olat.repository.RepositoryEntry;
+import org.olat.user.UserManager;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
@@ -60,11 +61,12 @@ public class AssessmentIdentityCourseNodeController extends BasicController {
 	private AssessmentForm assessmentForm;
 	private Controller subDetailsController;
 	private Controller detailsEditController;
-	private AssessedIdentityInfosController infosController;
 	
 	private final CourseNode courseNode;
 	private final Identity assessedIdentity;
 	
+	@Autowired
+	private UserManager userManager;
 	@Autowired
 	private BaseSecurity securityManager;
 	
@@ -78,10 +80,12 @@ public class AssessmentIdentityCourseNodeController extends BasicController {
 		
 		identityAssessmentVC = createVelocityContainer("identity_personal_node_infos");
 		identityAssessmentVC.contextPut("user", assessedIdentity.getUser());
+		identityAssessmentVC.contextPut("fullName", userManager.getUserDisplayName(assessedIdentity));
+		identityAssessmentVC.contextPut("courseNode", courseNode.getShortTitle());
 		
-		infosController = new AssessedIdentityInfosController(ureq, wControl, assessedIdentity);
-		listenTo(infosController);
-		identityAssessmentVC.put("identityInfos", infosController.getInitialComponent());
+		String courseNodeCssClass = CourseNodeFactory.getInstance()
+				.getCourseNodeConfigurationEvenForDisabledBB(courseNode.getType()).getIconCSSClass();
+		identityAssessmentVC.contextPut("courseNodeCss", courseNodeCssClass);
 		
 		ModuleConfiguration modConfig = courseNode.getModuleConfiguration();
 		String infoCoach = (String) modConfig.get(MSCourseNode.CONFIG_KEY_INFOTEXT_COACH);
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 6d6cd13f79a..adcc94063f5 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,13 +26,17 @@ 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.control.Controller;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.MainLayoutBasicController;
 import org.olat.core.gui.control.generic.dtabs.Activateable2;
+import org.olat.core.id.OLATResourceable;
+import org.olat.core.id.context.BusinessControlFactory;
 import org.olat.core.id.context.ContextEntry;
 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.modules.assessment.ui.AssessmentToolSecurityCallback;
 import org.olat.repository.RepositoryEntry;
@@ -92,11 +96,24 @@ public class AssessmentToolController extends MainLayoutBasicController implemen
 			doSelectUsersView(ureq);
 		}
 	}
-	
+
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		if(overviewCtrl == source) {
+			if(event == AssessmentCourseOverviewController.SELECT_USERS_EVENT) {
+				doSelectUsersView(ureq);
+			}
+		}
+		super.event(ureq, source, event);
+	}
+
 	private void doSelectUsersView(UserRequest ureq) {
 		removeAsListenerAndDispose(currentCtl);
 		
-		AssessmentIdentitiesCourseTreeController treeCtrl = new AssessmentIdentitiesCourseTreeController(ureq, getWindowControl(), stackPanel,
+		OLATResourceable ores = OresHelper.createOLATResourceableInstance("Users", 0l);
+		WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
+		addToHistory(ureq, bwControl);
+		AssessmentIdentitiesCourseTreeController treeCtrl = new AssessmentIdentitiesCourseTreeController(ureq, bwControl, stackPanel,
 				courseEntry, assessmentCallback);
 		listenTo(treeCtrl);
 		stackPanel.pushController(translate("users"), treeCtrl);
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/CourseNodeSelectionController.java b/src/main/java/org/olat/course/assessment/ui/tool/CourseNodeSelectionController.java
new file mode 100644
index 00000000000..4102b829d3d
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/ui/tool/CourseNodeSelectionController.java
@@ -0,0 +1,81 @@
+/**
+ * <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.assessment.ui.tool;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.tree.MenuTree;
+import org.olat.core.gui.components.tree.TreeModel;
+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.course.CourseFactory;
+import org.olat.course.ICourse;
+import org.olat.course.assessment.AssessmentHelper;
+import org.olat.course.assessment.ui.tool.event.CourseNodeEvent;
+import org.olat.course.nodes.CourseNode;
+import org.olat.repository.RepositoryEntry;
+
+/**
+ * 
+ * Initial date: 30.10.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class CourseNodeSelectionController extends BasicController {
+	
+	private final MenuTree menuTree;
+	
+	public CourseNodeSelectionController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry) {
+		super(ureq, wControl);
+	
+		ICourse course = CourseFactory.loadCourse(courseEntry);
+		
+		// Navigation menu
+		menuTree = new MenuTree("menuTree");
+		TreeModel tm = AssessmentHelper.assessmentTreeModel(course);
+		menuTree.setTreeModel(tm);
+		menuTree.setSelectedNodeId(tm.getRootNode().getIdent());
+		menuTree.addListener(this);
+		
+		VelocityContainer mainVC = createVelocityContainer("course_node_chooser");
+		mainVC.put("courseTree", menuTree);
+		putInitialPanel(mainVC);
+	}
+	
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		if (source == menuTree) {
+			if (event.getCommand().equals(MenuTree.COMMAND_TREENODE_CLICKED)) {
+				Object uo = menuTree.getSelectedNode().getUserObject();
+				if(uo instanceof CourseNode) {
+					CourseNode selectedNode = (CourseNode)uo;
+					fireEvent(ureq, new CourseNodeEvent(CourseNodeEvent.SELECT_COURSE_NODE, selectedNode.getIdent()));
+				}
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_content/course_node_chooser.html b/src/main/java/org/olat/course/assessment/ui/tool/_content/course_node_chooser.html
new file mode 100644
index 00000000000..24564e84732
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/ui/tool/_content/course_node_chooser.html
@@ -0,0 +1 @@
+$r.render("courseTree")
\ No newline at end of file
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 37a52450f2b..a1071244267 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,14 @@
-<h2>Overview</h2>
+<h2>$r.translate("assessment.tool.overview")</h2>
+<div class="row o_block">
+	<div class="col-sm-6">
+		#if($r.available("assessed.identities"))
+			$r.render("assessed.identities")
+		#end
+	</div>
+	<div class="col-sm-6">
+		Other links
+	</div>
+</div>
 <div class="row">
 	<div class="col-sm-6">
 		#if($r.available("toReview"))
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_personal_node_infos.html b/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_personal_node_infos.html
index 98387945179..86e3e2f2efa 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_personal_node_infos.html
+++ b/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_personal_node_infos.html
@@ -1,6 +1,5 @@
-#if($r.available("identityInfos"))
-	$r.render("identityInfos")
-#end
+<h2><i class="o_icon $courseNodeCss"> </i> $r.escapeHtml($courseNode) <small><i class="o_icon o_icon_user"> </i> $r.escapeHtml($fullName) </small></h2>
+
 #if($r.available("details")) 
 	$r.render("details")
 #end
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_content/user_infos_large.html b/src/main/java/org/olat/course/assessment/ui/tool/_content/user_infos_large.html
new file mode 100644
index 00000000000..6358348f651
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/ui/tool/_content/user_infos_large.html
@@ -0,0 +1,4 @@
+<div class="o_user_infos">
+	<div class="o_user_portrait">$r.render("portrait")</div>
+	<div class="o_user_infos_inner">$r.render("userShortDescription")</div>
+</div>
\ 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 02320e5e1a0..b35ed82517a 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
@@ -16,6 +16,8 @@ assessment.status.notStart=Nicht gestartet
 assessment.status.inProgress=Gestartet
 assessment.status.inReview=Korrigieren
 assessment.status.done=Bewertet
+assessment.tool.overview=Ubersicht
+assessment.tool.numOfAssessedIdentities={0} bewertete Benutzer
 table.entries=Entries
 waiting.review=Pending reviews
 no.certificate=Kein Zertifikat vorhanden
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/event/CourseNodeEvent.java b/src/main/java/org/olat/course/assessment/ui/tool/event/CourseNodeEvent.java
new file mode 100644
index 00000000000..a26ed88cec3
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/ui/tool/event/CourseNodeEvent.java
@@ -0,0 +1,46 @@
+/**
+ * <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.assessment.ui.tool.event;
+
+import org.olat.core.gui.control.Event;
+
+/**
+ * 
+ * Initial date: 30.10.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class CourseNodeEvent extends Event {
+
+	private static final long serialVersionUID = 4176476041353487088L;
+
+	public static final String SELECT_COURSE_NODE = "sel-cou-nod";
+	
+	private final String ident;
+	
+	public CourseNodeEvent(String cmd, String ident) {
+		super(cmd);
+		this.ident = ident;
+	}
+
+	public String getIdent() {
+		return ident;
+	}
+}
-- 
GitLab