From 78c054df1014d8199e8561af9fd3e82cae48c80f Mon Sep 17 00:00:00 2001
From: srosse <stephane.rosse@frentix.com>
Date: Tue, 21 Jul 2020 07:45:36 +0200
Subject: [PATCH] OO-4805: show a warning if assessed identity is locked

---
 .../cluster/lock/ClusterLocker.java           | 10 ++++++
 .../generic/modal/DialogBoxUIFactory.java     | 13 ++++---
 .../org/olat/core/util/coordinate/Locker.java |  9 ++++-
 ...ssessmentIdentityCourseNodeController.java | 24 +++++++------
 .../IdentityListCourseNodeController.java     | 34 ++++++++++++++++++-
 .../tool/tools/AbstractToolsController.java   |  4 +--
 6 files changed, 75 insertions(+), 19 deletions(-)

diff --git a/src/main/java/org/olat/commons/coordinate/cluster/lock/ClusterLocker.java b/src/main/java/org/olat/commons/coordinate/cluster/lock/ClusterLocker.java
index a010294d325..4b04c9af4b1 100644
--- a/src/main/java/org/olat/commons/coordinate/cluster/lock/ClusterLocker.java
+++ b/src/main/java/org/olat/commons/coordinate/cluster/lock/ClusterLocker.java
@@ -162,6 +162,16 @@ public class ClusterLocker implements Locker, GenericEventListener {
 		final String asset = OresHelper.createStringRepresenting(ores, locksubkey);
 		return clusterLockManager.isLocked(asset);
 	}
+	
+	@Override
+	public LockEntry getLockEntry(OLATResourceable ores, String locksubkey) {
+		final String asset = OresHelper.createStringRepresenting(ores, locksubkey);
+		LockImpl li = clusterLockManager.findLock(asset);
+		if(li == null) {
+			return null;
+		}
+		return new LockEntry(li.getAsset(), li.getCreationDate().getTime(), li.getOwner());
+	}
 
 	@Override
 	public Identity getLockedBy(OLATResourceable ores, String locksubkey) {
diff --git a/src/main/java/org/olat/core/gui/control/generic/modal/DialogBoxUIFactory.java b/src/main/java/org/olat/core/gui/control/generic/modal/DialogBoxUIFactory.java
index ab787ec982a..0efbdc0927e 100644
--- a/src/main/java/org/olat/core/gui/control/generic/modal/DialogBoxUIFactory.java
+++ b/src/main/java/org/olat/core/gui/control/generic/modal/DialogBoxUIFactory.java
@@ -37,6 +37,7 @@ import org.olat.core.logging.AssertException;
 import org.olat.core.util.Formatter;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
+import org.olat.core.util.coordinate.LockEntry;
 import org.olat.core.util.coordinate.LockResult;
 import org.olat.user.UserManager;
 
@@ -117,10 +118,7 @@ public class DialogBoxUIFactory {
 		if(lockEntry.isSuccess()){
 			throw new AssertException("do not create a 'is locked message' if lock was succesfull! concerns lock:"+lockEntry.getOwner());
 		}
-		String fullName = CoreSpringFactory.getImpl(UserManager.class).getUserDisplayName(lockEntry.getOwner());
-		String[] i18nParams = new String[] { StringHelper.escapeHtml(fullName),
-				Formatter.getInstance(ureq.getLocale()).formatTime(new Date(lockEntry.getLockAquiredTime())) };
-		String lockMsg = translator.translate(i18nLockMsgKey, i18nParams);
+		String lockMsg = getLockedMessage(ureq, lockEntry.getLockEntry(), i18nLockMsgKey, translator);
 		
 		Translator trans = Util.createPackageTranslator(DialogBoxUIFactory.class, ureq.getLocale());
 		List<String> okButton = new ArrayList<>();
@@ -131,6 +129,13 @@ public class DialogBoxUIFactory {
 		return ctrl;
 	}
 	
+	public static String getLockedMessage(UserRequest ureq, LockEntry lockEntry, String i18nLockMsgKey, Translator translator) {
+		String fullName = CoreSpringFactory.getImpl(UserManager.class).getUserDisplayName(lockEntry.getOwner());
+		String[] i18nParams = new String[] { StringHelper.escapeHtml(fullName),
+				Formatter.getInstance(ureq.getLocale()).formatTime(new Date(lockEntry.getLockAquiredTime())) };
+		return translator.translate(i18nLockMsgKey, i18nParams);
+	}
+	
 
 	/**
 	 * checks if this event from a OkCancel Dialog is an OK event.
diff --git a/src/main/java/org/olat/core/util/coordinate/Locker.java b/src/main/java/org/olat/core/util/coordinate/Locker.java
index 8b7423cae20..3654c9323a6 100644
--- a/src/main/java/org/olat/core/util/coordinate/Locker.java
+++ b/src/main/java/org/olat/core/util/coordinate/Locker.java
@@ -74,7 +74,14 @@ public interface Locker {
 	 * @return The identity which lock the resource or null.
 	 */
 	public Identity getLockedBy(OLATResourceable ores, String locksubkey);
-		
+	
+	/**
+	 * 
+	 * @param ores
+	 * @param locksubkey
+	 * @return
+	 */
+	public LockEntry getLockEntry(OLATResourceable ores, String locksubkey);
 	
 	/**
 	 * 
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 449160260e7..01256e93f17 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
@@ -22,6 +22,7 @@ package org.olat.course.assessment.ui.tool;
 import java.util.List;
 
 import org.olat.basesecurity.BaseSecurity;
+import org.olat.basesecurity.IdentityRef;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
@@ -30,7 +31,6 @@ 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.modal.DialogBoxController;
 import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory;
 import org.olat.core.id.Identity;
 import org.olat.core.id.IdentityEnvironment;
@@ -74,7 +74,6 @@ public class AssessmentIdentityCourseNodeController extends BasicController impl
 	private Controller identityInfosCtrl;
 	private Controller subDetailsController;
 	private Controller detailsEditController;
-	private DialogBoxController alreadyLockedDialogController;
 
 	private LockResult lockEntry;
 	private final CourseNode courseNode;
@@ -106,20 +105,19 @@ public class AssessmentIdentityCourseNodeController extends BasicController impl
 		
 		identityAssessmentVC = createVelocityContainer("identity_personal_node_infos");
 		initDetails();
+		
+		identityInfosCtrl = new AssessedIdentityLargeInfosController(ureq, wControl, assessedIdentity, course);
+		listenTo(identityInfosCtrl);
+		identityAssessmentVC.put("identityInfos", identityInfosCtrl.getInitialComponent());
 
 		//acquire lock and show dialog box on failure.
-		String lockSubKey = "AssessmentLock-NID::" + courseNode.getIdent() + "-IID::" + assessedIdentity.getKey();
+		String lockSubKey = lockKey(courseNode, assessedIdentity);
 		lockEntry = CoordinatorManager.getInstance().getCoordinator().getLocker().acquireLock(course, ureq.getIdentity(), lockSubKey);
 		if(!lockEntry.isSuccess()) {
-			alreadyLockedDialogController = DialogBoxUIFactory.createResourceLockedMessage(ureq, wControl, lockEntry, "assessmentLock", getTranslator());
-			listenTo(alreadyLockedDialogController);
-			alreadyLockedDialogController.activate();
+			String msg = DialogBoxUIFactory.getLockedMessage(ureq, lockEntry.getLockEntry(), "assessmentLock", getTranslator());
+			getWindowControl().setWarning(msg);
 		} else if(courseNode instanceof AssessableCourseNode) {
-			AssessableCourseNode aCourseNode = (AssessableCourseNode)courseNode;
-			
-			identityInfosCtrl = new AssessedIdentityLargeInfosController(ureq, wControl, assessedIdentity, course);
-			listenTo(identityInfosCtrl);
-			identityAssessmentVC.put("identityInfos", identityInfosCtrl.getInitialComponent());
+			AssessableCourseNode aCourseNode = (AssessableCourseNode)courseNode;	
 
 			// Add the users details controller
 			if (aCourseNode.hasDetails() && courseNodeDetails) {
@@ -140,6 +138,10 @@ public class AssessmentIdentityCourseNodeController extends BasicController impl
 		putInitialPanel(identityAssessmentVC);
 	}
 	
+	public static String lockKey(CourseNode node, IdentityRef identity) {
+		return "AssessmentLock-NID::" + node.getIdent() + "-IID::" + identity.getKey();
+	}
+	
 	public UserCourseEnvironment getCoachCourseEnvironment() {
 		return coachCourseEnv;
 	}
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java
index ac3e13dab44..0e27368c1bf 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java
@@ -59,6 +59,7 @@ import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowController;
 import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
 import org.olat.core.gui.control.generic.dtabs.Activateable2;
+import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory;
 import org.olat.core.id.Identity;
 import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.id.OLATResourceable;
@@ -68,6 +69,7 @@ import org.olat.core.id.context.StateEntry;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
 import org.olat.core.util.coordinate.CoordinatorManager;
+import org.olat.core.util.coordinate.LockEntry;
 import org.olat.core.util.event.GenericEventListener;
 import org.olat.core.util.resource.OresHelper;
 import org.olat.course.CourseFactory;
@@ -777,12 +779,42 @@ public class IdentityListCourseNodeController extends FormBasicController
 	
 	private void doSelect(UserRequest ureq, Identity assessedIdentity) {
 		List<AssessedIdentityElementRow> rows = usersTableModel.getObjects();
+		AssessedIdentityElementRow selectedRow = null;
 		for(AssessedIdentityElementRow row:rows) {
 			if(assessedIdentity.getKey().equals(row.getIdentityKey())) {
-				doSelect(ureq, row);
+				selectedRow = row;
 				break;
 			}
 		}
+		
+		if(selectedRow != null && !isAssessedIdentityLocked(ureq, assessedIdentity)) {
+			doSelect(ureq, selectedRow);
+		}
+	}
+	
+	/**
+	 * Preventive check if the identity is already locked by an other
+	 * user and show a warning message if needed.
+	 *  
+	 * @param ureq The user request
+	 * @param assessedIdentity The identity to assess
+	 * @return
+	 */
+	private boolean isAssessedIdentityLocked(UserRequest ureq, Identity assessedIdentity) {
+		if(courseNode.getParent() == null) return false;
+
+		ICourse course = CourseFactory.loadCourse(courseEntry);
+		String locksubkey = AssessmentIdentityCourseNodeController.lockKey(courseNode, assessedIdentity);
+		if(CoordinatorManager.getInstance().getCoordinator().getLocker().isLocked(course, locksubkey)) {
+			LockEntry lock = CoordinatorManager.getInstance().getCoordinator().getLocker().getLockEntry(course, locksubkey);
+			if(lock != null && lock.getOwner() != null && !lock.getOwner().equals(getIdentity())) {
+				String msg = DialogBoxUIFactory.getLockedMessage(ureq, lock, "assessmentLock", getTranslator());
+				getWindowControl().setWarning(msg);
+				return true;
+			}
+		}
+		
+		return false;
 	}
 	
 	private Controller doSelect(UserRequest ureq, AssessedIdentityElementRow row) {
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/tools/AbstractToolsController.java b/src/main/java/org/olat/course/assessment/ui/tool/tools/AbstractToolsController.java
index 5a23115242c..09bce8b02ab 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/tools/AbstractToolsController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/tools/AbstractToolsController.java
@@ -121,8 +121,8 @@ public abstract class AbstractToolsController extends BasicController {
 			}
 			lastLink = link;	
 		}
-		if(links.size() > 0 && "-".equals(links.get(links.size() - 1))) {
-			links.remove(links.size() -1);//no trialing separator
+		if(!links.isEmpty() && "-".equals(links.get(links.size() - 1))) {
+			links.remove(links.size() -1);//no trailing separator
 		}
 
 		mainVC.contextPut("links", links);
-- 
GitLab