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 a76cd8d5d22d93d828e44aefb1d5cc028bbcc3d1..a9d25c7b12865f93d9c9a6a38555ff064dd4f5ac 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 @@ -150,6 +150,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(), li.getWindowId()); + } @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 ab787ec982aa44f6218f14975cdc296eaaa7e719..0efbdc0927e62cb6ff95eb6f6e36dc35f27bfd85 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 da91b37ab0711d86b31fcd7fbeb21a6f6e734835..9143c50ddc8da51f309b37239c646882c71ac1a5 100644 --- a/src/main/java/org/olat/core/util/coordinate/Locker.java +++ b/src/main/java/org/olat/core/util/coordinate/Locker.java @@ -75,7 +75,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 928b8c75c6b5a796704f81fd091638a2b8dfc79d..ee1677338694a5c70a139c743b76301acd612753 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; @@ -75,7 +75,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; @@ -109,19 +108,18 @@ 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, getWindow()); 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 { - identityInfosCtrl = new AssessedIdentityLargeInfosController(ureq, wControl, assessedIdentity, course); - listenTo(identityInfosCtrl); - identityAssessmentVC.put("identityInfos", identityInfosCtrl.getInitialComponent()); - // Add the users details controller AssessmentConfig assessmentConfig = courseAssessmentService.getAssessmentConfig(courseNode); if (assessmentConfig.hasEditableDetails() && courseNodeDetails) { @@ -143,6 +141,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 77567666cd25a97af95fd8b59de15152ba8847ce..26dc189640769fb5e0dabcd85232f33b1ec39535 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 @@ -60,6 +60,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; @@ -69,6 +70,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.mail.ContactList; import org.olat.core.util.mail.ContactMessage; @@ -826,12 +828,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 8345ac26f0c7c90f1007b4e8ec9da11f38a47195..90f79106b66bb0ceccab8b0209cb4938af0ec22f 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 @@ -126,8 +126,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); diff --git a/src/main/java/org/olat/modules/openmeetings/manager/OpenMeetingsManagerImpl.java b/src/main/java/org/olat/modules/openmeetings/manager/OpenMeetingsManagerImpl.java index 29a77d46cea7429eb3a2ee8023eb3ecb1879b2ed..b76b5ad7b82bc62e954ba10df7abd5460237f9a1 100644 --- a/src/main/java/org/olat/modules/openmeetings/manager/OpenMeetingsManagerImpl.java +++ b/src/main/java/org/olat/modules/openmeetings/manager/OpenMeetingsManagerImpl.java @@ -540,12 +540,16 @@ public class OpenMeetingsManagerImpl implements OpenMeetingsManager, UserDataDel @Override public boolean deleteRoom(OpenMeetingsRoom room) { + return deleteRoom(room, false); + } + + private boolean deleteRoom(OpenMeetingsRoom room, boolean force) { try { String adminSID = adminLogin(); RoomServicePortType roomWs = getRoomWebService(); long ret = roomWs.deleteRoom(adminSID, room.getRoomId()); boolean ok = ret > 0; - if(ok && room.getReference() != null) { + if((ok || force) && room.getReference() != null) { openMeetingsDao.delete(room.getReference()); } return ok; @@ -715,7 +719,7 @@ public class OpenMeetingsManagerImpl implements OpenMeetingsManager, UserDataDel boolean allOk = true; OpenMeetingsRoom room = getLocalRoom(group, null, null); if(room != null) { - allOk &= deleteRoom(room); + allOk &= deleteRoom(room, true); } return allOk; } diff --git a/src/main/java/org/olat/modules/scorm/assessment/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/scorm/assessment/_i18n/LocalStrings_de.properties index d4c20af34e443afb7a2b300936103b50ec58e9c3..2b95f0ac5becd0e442e8c3a537e9b57ace2877b9 100644 --- a/src/main/java/org/olat/modules/scorm/assessment/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/scorm/assessment/_i18n/LocalStrings_de.properties @@ -38,13 +38,13 @@ cmi.student_preference.speed=Geschwindigkeit cmi.student_preference.text=Texteinstellung cmi.suspend_data=Zustandsdaten bei Unterbrechung cmis.column.header.itemId=Bausteine -cmis.column.header.key=Schl\u00FCssel +cmis.column.header.key=Schl\u00fcssel cmis.column.header.translatedKey=Frage cmis.column.header.value=Wert summary.column.header.assesspoints=Punkte summary.column.header.date=Datum summary.column.header.details=Details summary.column.header.duration=Dauer -reset=SCORM Resultaten zur\u00FCcksetzen -reset.title=SCORM Resultate zur\u00FCcksetzen -reset.text=Wollen Sie die SCORM Resultate von <b>{0}</b> wirklich zur\u00FCcksetzen? Dies beinhaltet alle Bewegungsdaten dieses SCORM Moduls inklusive allf\u00e4lliger Testdaten. Als abgeschlossen markierte SCORM Module k\u00f6nnen so erneut aufgerufen und durchgef\u00fchrt werden. Die beim letzten Versuch \u00fcbertragenen Punkte werden nicht ver\u00e4ndert. +reset=SCORM Resultate zur\u00fccksetzen +reset.title=SCORM Resultate zur\u00fccksetzen +reset.text=Wollen Sie die SCORM Resultate von <b>{0}</b> wirklich zur\u00fccksetzen? Dies beinhaltet alle Bewegungsdaten dieses SCORM Moduls inklusive allf\u00e4lliger Testdaten. Als abgeschlossen markierte SCORM Module k\u00f6nnen so erneut aufgerufen und durchgef\u00fchrt werden. Die beim letzten Versuch \u00fcbertragenen Punkte werden nicht ver\u00e4ndert.