/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <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> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. */ package org.olat.course.editor; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.badge.Badge.Level; import org.olat.core.gui.components.dropdown.Dropdown; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.panel.MainPanel; import org.olat.core.gui.components.panel.SimpleStackedPanel; import org.olat.core.gui.components.panel.StackedPanel; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.components.stack.TooledStackedPanel.Align; import org.olat.core.gui.components.tabbedpane.TabbedPane; import org.olat.core.gui.components.tree.MenuTree; import org.olat.core.gui.components.tree.TreeDropEvent; import org.olat.core.gui.components.tree.TreeEvent; 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; import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.gui.control.generic.modal.DialogBoxController; import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.gui.control.generic.tabbable.ActivateableTabbableDefaultController; import org.olat.core.gui.control.generic.tabbable.TabbableController; import org.olat.core.gui.control.generic.wizard.Step; import org.olat.core.gui.control.generic.wizard.StepRunnerCallback; import org.olat.core.gui.control.generic.wizard.StepsMainRunController; import org.olat.core.gui.control.generic.wizard.StepsRunContext; import org.olat.core.gui.control.winmgr.JSCommand; import org.olat.core.id.OLATResourceable; import org.olat.core.id.context.BusinessControlFactory; import org.olat.core.id.context.ContextEntry; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.logging.activity.ActionType; import org.olat.core.logging.activity.CourseLoggingAction; import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; 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.coordinate.LockRemovedEvent; import org.olat.core.util.coordinate.LockResult; import org.olat.core.util.event.GenericEventListener; import org.olat.core.util.event.MultiUserEvent; import org.olat.core.util.nodes.INode; import org.olat.core.util.resource.OLATResourceableJustBeforeDeletedEvent; import org.olat.core.util.resource.OresHelper; import org.olat.core.util.tree.TreeVisitor; import org.olat.core.util.tree.Visitor; import org.olat.core.util.vfs.VFSContainer; import org.olat.course.CorruptedCourseException; import org.olat.course.CourseFactory; import org.olat.course.DisposedCourseRestartController; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentModeManager; import org.olat.course.editor.PublishStepCatalog.CategoryLabel; import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.CourseNodeConfiguration; import org.olat.course.nodes.CourseNodeFactory; import org.olat.course.nodes.cl.ui.wizard.CheckListStepRunnerCallback; 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.ui.RepositoryEntryRuntimeController.ToolbarAware; import org.olat.util.logging.activity.LoggingResourceable; import org.springframework.beans.factory.annotation.Autowired; /** * Description:<br> * The editor controller generates a view which is used to manipulate a course. * The changes are all applied on the course editor model and not on the course * runtime structure. Changes must be published explicitely. This mechanism is * also part of the editor.<br> * The editor uses the full window (menu - content - tool) and has a close link * in the toolbox. * <P> * * @author Felix Jost * @author BPS (<a href="http://www.bps-system.de/">BPS Bildungsportal Sachsen GmbH</a>) */ 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_"; private static final String CMD_COPYNODE = "copyn"; private static final String CMD_MOVENODE = "moven"; private static final String CMD_DELNODE = "deln"; private static final String CMD_PUBLISH = "pbl"; private static final String CMD_COURSEPREVIEW = "cprev"; protected static final String CMD_MULTI_SP = "cmp.multi.sp"; protected static final String CMD_MULTI_CHECKLIST = "cmp.multi.checklist"; // NLS support private static final String NLS_COMMAND_COURSEPREVIEW = "command.coursepreview"; private static final String NLS_COMMAND_PUBLISH = "command.publish"; private static final String NLS_HEADER_INSERTNODES = "header.insertnodes"; private static final String NLS_COMMAND_DELETENODE_HEADER = "command.deletenode.header"; private static final String NLS_COMMAND_DELETENODE = "command.deletenode"; private static final String NLS_COMMAND_MOVENODE = "command.movenode"; private static final String NLS_COMMAND_COPYNODE = "command.copynode"; private static final String NLS_DELETENODE_SUCCESS = "deletenode.success"; private static final String NLS_START_HELP_WIZARD = "start.help.wizard"; private static final String NLS_INSERTNODE_TITLE = "insertnode.title"; private static final String NLS_DELETENODE_ERROR_SELECTFIRST = "deletenode.error.selectfirst"; 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; private TabbedPane tabbedNodeConfig; private CourseEditorTreeModel cetm; private TabbableController nodeEditCntrllr; private StepsMainRunController publishStepsController; private StepsMainRunController checklistWizard; private PreviewConfigController previewController; private MoveCopySubtreeController moveCopyController; private DialogBoxController deleteDialogController; private LayoutMain3ColsController columnLayoutCtr; private AlternativeCourseNodeController alternateCtr; private EditorStatusController statusCtr; private ChooseNodeController chooseNodeTypeCtr; private QuickPublishController quickPublishCtr; private LockResult lockEntry; private EditorUserCourseEnvironmentImpl euce; private Dropdown nodeTools; private Link undelButton, alternativeLink, statusLink; private Link previewLink, publishLink, closeLink; private Link createNodeLink, deleteNodeLink, moveNodeLink, copyNodeLink; private CloseableModalController cmc; private CloseableCalloutWindowController calloutCtrl; private TooledStackedPanel stackPanel; private MultiSPController multiSPChooserCtr; private final OLATResourceable ores; private RepositoryEntry repoEntry; private static final OLog log = Tracing.createLoggerFor(EditorMainController.class); private final static String RELEASE_LOCK_AT_CATCH_EXCEPTION = "Must release course lock since an exception occured in " + EditorMainController.class; @Autowired private AssessmentModeManager assessmentModeMgr; /** * Constructor for the course editor controller * * @param ureq The user request * @param wControl The window controller * @param course The course */ public EditorMainController(UserRequest ureq, WindowControl wControl, TooledStackedPanel toolbar, ICourse course, CourseNode selectedNode) { super(ureq,wControl); this.ores = OresHelper.clone(course); this.stackPanel = toolbar; // OLAT-4955: setting the stickyActionType here passes it on to any controller defined in the scope of the editor, // basically forcing any logging action called within the course editor to be of type 'admin' getUserActivityLogger().setStickyActionType(ActionType.admin); addLoggingResourceable(LoggingResourceable.wrap(course)); // try to acquire edit lock for this course. lockEntry = CoordinatorManager.getInstance().getCoordinator().getLocker().acquireLock(ores, ureq.getIdentity(), CourseFactory.COURSE_EDITOR_LOCK); if(CourseFactory.isCourseEditSessionOpen(ores.getResourceableId())) { MainPanel empty = new MainPanel("empty"); putInitialPanel(empty); return; } OLATResourceable lockEntryOres = OresHelper.createOLATResourceableInstance(LockEntry.class, 0l); CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, getIdentity(), lockEntryOres); try { ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_EDITOR_OPEN, getClass()); if (!lockEntry.isSuccess()) { MainPanel empty = new MainPanel("empty"); putInitialPanel(empty); } else { course = CourseFactory.openCourseEditSession(ores.getResourceableId()); CourseGroupManager cgm = course.getCourseEnvironment().getCourseGroupManager(); repoEntry = cgm.getCourseEntry(); main = createVelocityContainer("index"); //must be true for deleted course node main.setDomReplacementWrapperRequired(true); Controller courseCloser = new DisposedCourseRestartController(ureq, wControl, repoEntry); Controller disposedRestartController = new LayoutMain3ColsController(ureq, wControl, courseCloser); setDisposedMsgController(disposedRestartController); undelButton = LinkFactory.createButton("undeletenode.button", main, this); menuTree = new MenuTree("luTree"); menuTree.setExpandSelectedNode(false); menuTree.setDragEnabled(true); menuTree.setDropEnabled(true); menuTree.setDropSiblingEnabled(true); menuTree.setDndAcceptJSMethod("treeAcceptDrop_notWithChildren"); menuTree.setElementCssClass("o_editor_menu"); /* * create editor user course environment for enhanced syntax/semantic * checks. Initialize it with the current course node id, which is not set * yet. Furthermore the course is refreshed, e.g. as it get's loaded by * XSTREAM constructors are not called, but transient data must be * caculated and initialized */ cetm = course.getEditorTreeModel(); CourseEditorEnv cev = new CourseEditorEnvImpl(cetm, cgm, getLocale()); euce = new EditorUserCourseEnvironmentImpl(cev, getWindowControl()); euce.getCourseEditorEnv().setCurrentCourseNodeId(null); menuTree.setTreeModel(cetm); menuTree.setOpenNodeIds(Collections.singleton(cetm.getRootNode().getIdent())); menuTree.addListener(this); tabbedNodeConfig = new TabbedPane("tabbedNodeConfig", getLocale()); tabbedNodeConfig.setElementCssClass("o_node_config"); main.put(tabbedNodeConfig.getComponentName(), tabbedNodeConfig); alternativeLink = LinkFactory.createButton("alternative", main, this); main.put("alternative", alternativeLink); columnLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), menuTree, main, "course" + course.getResourceableId()); columnLayoutCtr.addCssClassToMain("o_editor"); listenTo(columnLayoutCtr); StackedPanel initialPanel = putInitialPanel(new SimpleStackedPanel("coursePanel", "o_edit_mode")); initialPanel.setContent(columnLayoutCtr.getInitialComponent()); //tools statusLink = LinkFactory.createToolLink("status", translate("status"), this, null); statusLink.setUserObject(new EditedCourseStatus()); createNodeLink = LinkFactory.createToolLink(NLS_HEADER_INSERTNODES, translate(NLS_HEADER_INSERTNODES), this, "o_icon_add"); createNodeLink.setElementCssClass("o_sel_course_editor_create_node"); createNodeLink.setDomReplacementWrapperRequired(false); nodeTools = new Dropdown("insertNodes", NLS_COMMAND_DELETENODE_HEADER, false, getTranslator()); nodeTools.setIconCSS("o_icon o_icon_customize"); nodeTools.setElementCssClass("o_sel_course_editor_change_node"); deleteNodeLink = LinkFactory.createToolLink(CMD_DELNODE, translate(NLS_COMMAND_DELETENODE), this, "o_icon_delete_item"); nodeTools.addComponent(deleteNodeLink); moveNodeLink = LinkFactory.createToolLink(CMD_MOVENODE, translate(NLS_COMMAND_MOVENODE), this, "o_icon_move"); moveNodeLink.setElementCssClass("o_sel_course_editor_move_node"); nodeTools.addComponent(moveNodeLink); copyNodeLink = LinkFactory.createToolLink(CMD_COPYNODE, translate(NLS_COMMAND_COPYNODE), this, "o_icon_copy"); nodeTools.addComponent(copyNodeLink); 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(); updateCourseStatusMessages(getLocale(), courseStatus); // add as listener to course so we are being notified about course events: // - deleted events CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, ureq.getIdentity(), course); // activate course root node String nodeIdent = cetm.getRootNode().getIdent(); if(selectedNode != null) { CourseEditorTreeNode editorNode = cetm.getCourseEditorNodeContaining(selectedNode); if(editorNode != null) { nodeIdent = editorNode.getIdent(); } } menuTree.setSelectedNodeId(nodeIdent); updateViewForSelectedNodeId(ureq, nodeIdent); } } catch (RuntimeException e) { log.warn(RELEASE_LOCK_AT_CATCH_EXCEPTION+" [in <init>]", e); dispose(); throw e; } } @Override public void initToolbar() { stackPanel.addTool(createNodeLink, Align.left); stackPanel.addTool(nodeTools, Align.left); stackPanel.addTool(statusLink, Align.right); stackPanel.addTool(previewLink, Align.right); stackPanel.addTool(publishLink, Align.right); } @Override public boolean requestForClose(UserRequest ureq) { boolean immediateClose = true; try { ICourse course = CourseFactory.loadCourse(ores.getResourceableId()); if(hasPublishableChanges(course)) { doQuickPublish(ureq, course); immediateClose = false; } } catch (CorruptedCourseException | NullPointerException e) { logError("Error request on close: " + ores, e); } 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) */ @Override public void event(UserRequest ureq, Component source, Event event) { try { ICourse course = CourseFactory.getCourseEditSession(ores.getResourceableId()); if (source == menuTree) { if (event.getCommand().equals(MenuTree.COMMAND_TREENODE_CLICKED)) { // goto node in edit mode TreeEvent te = (TreeEvent) event; String nodeId = te.getNodeId(); updateViewForSelectedNodeId(ureq, nodeId); } else if(event.getCommand().equals(MenuTree.COMMAND_TREENODE_DROP)) { TreeDropEvent te = (TreeDropEvent) event; dropNodeAsChild(ureq, course, te.getDroppedNodeId(), te.getTargetNodeId(), te.isAsChild(), te.isAtTheEnd()); } } else if (source == main) { if (event.getCommand().startsWith(NLS_START_HELP_WIZARD)) { doStartHelpWizard(ureq, event); } } else if (source == undelButton){ doUndelete(ureq, course); } else if(source == alternativeLink) { CourseNode chosenNode = (CourseNode)alternativeLink.getUserObject(); askForAlternative(ureq, chosenNode); } else if(previewLink == source) { launchPreview(ureq, course); } else if(publishLink == source) { launchPublishingWizard(ureq, course, false); } else if(closeLink == source) { doReleaseEditLock(); fireEvent(ureq, Event.DONE_EVENT); } else if(createNodeLink == source) { doOpenNodeTypeChooser(ureq); } else if(deleteNodeLink == source) { doDeleteNode(ureq); } else if(moveNodeLink == source) { doMove(ureq, course, false); } else if(copyNodeLink == source) { doMove(ureq, course, true); } else if(statusLink == source) { doOpenStatusOverview(ureq); } } catch (RuntimeException e) { log.warn(RELEASE_LOCK_AT_CATCH_EXCEPTION+" [in event(UserRequest,Component,Event)]", e); dispose(); throw e; } } private void askForAlternative(UserRequest ureq, CourseNode chosenNode) { removeAsListenerAndDispose(alternateCtr); removeAsListenerAndDispose(cmc); alternateCtr = new AlternativeCourseNodeController(ureq, getWindowControl(), chosenNode); listenTo(alternateCtr); cmc = new CloseableModalController(getWindowControl(), translate("close"), alternateCtr.getInitialComponent(), true, translate("alternative.choose")); listenTo(cmc); cmc.activate(); } /** * The following operation are done: * <ul> * <li>create a new instance of the replacement type * <li>add the new element below the original element * <li>copy the element title, description and the generic configuration options * <li>copy the access, visibility and scoring rules (easy and expert) * <li>optionally copy some other configuration if this is possible at all * <li>move all child elements from the original to the replacement element * <li>mark the original element as deleted * </ul> * * @param chosenNode * @param selectAlternative */ private void doCreateAlternateBuildingBlock(UserRequest ureq, ICourse course, CourseNode chosenNode, String selectAlternative) { if(!StringHelper.containsNonWhitespace(selectAlternative)) return; //create the alternative node CourseNodeConfiguration newConfig = CourseNodeFactory.getInstance().getCourseNodeConfiguration(selectAlternative); CourseNode newNode = newConfig.getInstance(); //copy configurations chosenNode.copyConfigurationTo(newNode, course); //insert the node CourseEditorTreeNode cetn = (CourseEditorTreeNode)cetm.getNodeById(chosenNode.getIdent()); CourseEditorTreeNode parentNode = (CourseEditorTreeNode)cetn.getParent(); int position = cetn.getPosition() + 1; CourseEditorTreeNode newCetn = course.getEditorTreeModel().insertCourseNodeAt(newNode, parentNode.getCourseNode(), position); doInsert(ureq, newNode); //copy the children while(cetn.getChildCount() > 0) { CourseEditorTreeNode childNode = (CourseEditorTreeNode)cetn.getChildAt(0); newCetn.addChild(childNode); } //set all dirty TreeVisitor tv = new TreeVisitor( new Visitor() { public void visit(INode node) { ((CourseEditorTreeNode)node).setDirty(true); } }, newCetn, true); tv.visitAll(); //mark as deleted doDelete(course, chosenNode.getIdent()); //save CourseFactory.saveCourseEditorTreeModel(course.getResourceableId()); } /** * helper to update menu tree, content area, tools to a selected tree node * @param ureq * @param nodeId */ private void updateViewForSelectedNodeId(UserRequest ureq, String nodeId) { CourseEditorTreeNode cetn = (CourseEditorTreeNode) cetm.getNodeById(nodeId); // udpate the current node in the course editor environment euce.getCourseEditorEnv().setCurrentCourseNodeId(nodeId); // Start necessary controller for selected node if (cetn.isDeleted()) { tabbedNodeConfig.setVisible(false); deleteNodeLink.setEnabled(false); moveNodeLink.setEnabled(false); copyNodeLink.setEnabled(false); if (((CourseEditorTreeNode) cetn.getParent()).isDeleted()) { main.setPage(VELOCITY_ROOT + "/deletednode.html"); } else { main.setPage(VELOCITY_ROOT + "/undeletenode.html"); } } else { tabbedNodeConfig.setVisible(true); deleteNodeLink.setEnabled(true); moveNodeLink.setEnabled(true); copyNodeLink.setEnabled(true); initNodeEditor(ureq, cetn.getCourseNode()); main.setPage(VELOCITY_ROOT + "/index.html"); } } /** * Initializes the node edit tabbed pane and its controller for this * particular node * * @param ureq * @param chosenNode * @param groupMgr */ private void initNodeEditor(UserRequest ureq, CourseNode chosenNode) { ICourse course = CourseFactory.getCourseEditSession(ores.getResourceableId()); tabbedNodeConfig.removeAll(); // dispose old one, if there was one removeAsListenerAndDispose(nodeEditCntrllr); String type = chosenNode.getType(); CourseNodeConfiguration cnConfig = CourseNodeFactory.getInstance().getCourseNodeConfigurationEvenForDisabledBB(type); if (cnConfig.isEnabled()) { nodeEditCntrllr = chosenNode.createEditController(ureq, getWindowControl(), stackPanel, course, euce); listenTo(nodeEditCntrllr); nodeEditCntrllr.addTabs(tabbedNodeConfig); } boolean disabled = !cnConfig.isEnabled(); boolean deprecated = cnConfig.isDeprecated(); main.contextPut("courseNodeDisabled", disabled); main.contextPut("courseNodeDeprecated", deprecated); alternativeLink.setVisible((disabled || deprecated) && !cnConfig.getAlternativeCourseNodes().isEmpty()); alternativeLink.setUserObject(chosenNode); String nodeCssClass = null; if (this.cetm.getRootNode().getIdent().equals(chosenNode.getIdent())) { // Special case for root node nodeCssClass = "o_CourseModule_icon"; } else { nodeCssClass = cnConfig.getIconCSSClass(); } main.contextPut("courseNodeCss", nodeCssClass); main.contextPut("courseNode", chosenNode); } /** * Initializes the node edit tabbed pane and its controller for this * particular node * * @param ureq * @param chosenNode * @param groupMgr */ private void jumpToNodeEditor(String activatorIdent, UserRequest ureq, CourseNode chosenNode) { initNodeEditor(ureq, chosenNode); if (nodeEditCntrllr instanceof ActivateableTabbableDefaultController) { OLATResourceable activeOres = OresHelper.createOLATResourceableInstanceWithoutCheck(activatorIdent, 0l); List<ContextEntry> entries = BusinessControlFactory.getInstance().createCEListFromString(activeOres); ((ActivateableTabbableDefaultController) nodeEditCntrllr).activate(ureq, entries, null); } } /** * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, * org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event) */ @Override public void event(UserRequest ureq, Controller source, Event event) { try { ICourse course = CourseFactory.getCourseEditSession(ores.getResourceableId()); if (source == nodeEditCntrllr) { // event from the tabbed pane (any tab) if (event == NodeEditController.NODECONFIG_CHANGED_EVENT) { // if the user changed the name of the node, we need to update the tree also. // the event is too generic to find out what happened -> update tree in all cases (applies to ajax mode only) menuTree.setDirty(true); cetm.nodeConfigChanged(menuTree.getSelectedNode()); CourseFactory.saveCourseEditorTreeModel(course.getResourceableId()); euce.getCourseEditorEnv().validateCourse(); StatusDescription[] courseStatus = euce.getCourseEditorEnv().getCourseStatus(); updateCourseStatusMessages(ureq.getLocale(), courseStatus); } } else if (source == statusCtr) { if (event.getCommand().startsWith(NLS_START_HELP_WIZARD)) { doStartHelpWizard(ureq, event); } calloutCtrl.deactivate(); cleanUp(); } else if (source == chooseNodeTypeCtr) { cmc.deactivate(); String cmd = event.getCommand(); if(cmd.startsWith(TB_ACTION)) { CourseNode newNode = chooseNodeTypeCtr.getCreatedNode(); cleanUp(); doInsert(ureq, newNode); } else if(CMD_MULTI_SP.equals(cmd)) { cleanUp(); launchSinglePagesWizard(ureq, course); } else if(CMD_MULTI_CHECKLIST.equals(cmd)) { cleanUp(); launchChecklistsWizard(ureq); } } 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 //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); cleanUpNodeController(); 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 removeAsListenerAndDispose(previewController); previewController = null; } } else if (source == checklistWizard) { if(event == Event.CANCELLED_EVENT || event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { getWindowControl().pop(); removeAsListenerAndDispose(checklistWizard); checklistWizard = null; if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { menuTree.setDirty(true); CourseFactory.saveCourseEditorTreeModel(course.getResourceableId()); euce.getCourseEditorEnv().validateCourse(); StatusDescription[] courseStatus = euce.getCourseEditorEnv().getCourseStatus(); updateCourseStatusMessages(ureq.getLocale(), courseStatus); } } } else if (source == cmc) { cleanUp(); } else if (source == moveCopyController) { cmc.deactivate(); if (event == Event.DONE_EVENT) { menuTree.setDirty(true); // setDirty when moving // Repositioning to move/copy course node String nodeId = moveCopyController.getCopyNodeId(); if (nodeId != null) { menuTree.setSelectedNodeId(nodeId); euce.getCourseEditorEnv().setCurrentCourseNodeId(nodeId); CourseNode copyNode = cetm.getCourseNode(nodeId); initNodeEditor(ureq, copyNode); } euce.getCourseEditorEnv().validateCourse(); StatusDescription[] courseStatus = euce.getCourseEditorEnv().getCourseStatus(); updateCourseStatusMessages(ureq.getLocale(), courseStatus); } else if (event == Event.FAILED_EVENT) { getWindowControl().setError("Error in copy of subtree."); } else if (event == Event.CANCELLED_EVENT) { // user canceled } //aggressive clean-up cleanUp(); } else if (source == deleteDialogController){ removeAsListenerAndDispose(deleteDialogController); deleteDialogController = null; if (DialogBoxUIFactory.isYesEvent(event)){ // delete confirmed String ident = menuTree.getSelectedNode().getIdent(); // udpate the current node in the course editor environment doDelete(course, ident); } else { tabbedNodeConfig.setVisible(true); } } else if (source == multiSPChooserCtr) { cmc.deactivate(); removeAsListenerAndDispose(cmc); removeAsListenerAndDispose(multiSPChooserCtr); cmc = null; if(event == Event.CHANGED_EVENT) { menuTree.setDirty(true); euce.getCourseEditorEnv().validateCourse(); StatusDescription[] courseStatus = euce.getCourseEditorEnv().getCourseStatus(); updateCourseStatusMessages(ureq.getLocale(), courseStatus); } } else if (source == alternateCtr) { cmc.deactivate(); if(event == Event.DONE_EVENT) { CourseNode chosenNode = alternateCtr.getCourseNode(); String selectAlternative = alternateCtr.getSelectedAlternative(); doCreateAlternateBuildingBlock(ureq, course, chosenNode, selectAlternative); } removeAsListenerAndDispose(cmc); removeAsListenerAndDispose(alternateCtr); cmc = null; alternateCtr = null; } } catch (RuntimeException e) { log.warn(RELEASE_LOCK_AT_CATCH_EXCEPTION+" [in event(UserRequest,Controller,Event)]", e); this.dispose(); throw e; } } /** * Aggressive clean-up of popup controllers */ private void cleanUp() { removeAsListenerAndDispose(moveCopyController); removeAsListenerAndDispose(multiSPChooserCtr); removeAsListenerAndDispose(chooseNodeTypeCtr); removeAsListenerAndDispose(alternateCtr); removeAsListenerAndDispose(calloutCtrl); removeAsListenerAndDispose(statusCtr); removeAsListenerAndDispose(cmc); moveCopyController = null; chooseNodeTypeCtr = null; multiSPChooserCtr = null; alternateCtr = null; calloutCtrl = null; statusCtr = null; cmc = null; } private void cleanUpNodeController() { tabbedNodeConfig.removeAll(); // dispose old one, if there was one removeAsListenerAndDispose(nodeEditCntrllr); } 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; TreeNode tn = menuTree.getSelectedNode(); if (tn == null) { showError(NLS_MOVECOPYNODE_ERROR_SELECTFIRST); return; } if (tn.getParent() == null) { showError(NLS_MOVECOPYNODE_ERROR_ROOTNODE); return; } removeAsListenerAndDispose(moveCopyController); removeAsListenerAndDispose(cmc); CourseEditorTreeNode cetn = cetm.getCourseEditorNodeById(tn.getIdent()); moveCopyController = new MoveCopySubtreeController(ureq, getWindowControl(), course, cetn, copy); listenTo(moveCopyController); cmc = new CloseableModalController(getWindowControl(), translate("close"), moveCopyController.getInitialComponent(), true, translate(NLS_INSERTNODE_TITLE)); listenTo(cmc); 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); cetm.markDeleted(activeNode); menuTree.setDirty(true); CourseFactory.saveCourseEditorTreeModel(course.getResourceableId()); tabbedNodeConfig.removeAll(); tabbedNodeConfig.setVisible(false); deleteNodeLink.setEnabled(false); moveNodeLink.setEnabled(false); copyNodeLink.setEnabled(false); main.setPage(VELOCITY_ROOT + "/undeletenode.html"); // offer undelete showInfo(NLS_DELETENODE_SUCCESS); // validate course and update course status euce.getCourseEditorEnv().validateCourse(); StatusDescription[] courseStatus = euce.getCourseEditorEnv().getCourseStatus(); updateCourseStatusMessages(getLocale(), courseStatus); ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_EDITOR_NODE_DELETED, getClass(), LoggingResourceable.wrap(activeNode)); } private void doUndelete(UserRequest ureq, ICourse course) { String ident = menuTree.getSelectedNode().getIdent(); CourseEditorTreeNode activeNode = (CourseEditorTreeNode) cetm.getNodeById(ident); euce.getCourseEditorEnv().setCurrentCourseNodeId(activeNode.getIdent()); CourseFactory.saveCourseEditorTreeModel(course.getResourceableId()); cetm.markUnDeleted(activeNode); menuTree.setDirty(true); // show edit panels again initNodeEditor(ureq, activeNode.getCourseNode()); tabbedNodeConfig.setVisible(true); deleteNodeLink.setEnabled(true); moveNodeLink.setEnabled(true); copyNodeLink.setEnabled(true); main.setPage(VELOCITY_ROOT + "/index.html"); // validate course and update course status euce.getCourseEditorEnv().validateCourse(); StatusDescription[] courseStatus = euce.getCourseEditorEnv().getCourseStatus(); updateCourseStatusMessages(ureq.getLocale(), courseStatus); // do logging ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_EDITOR_NODE_RESTORED, getClass(), LoggingResourceable.wrap(activeNode.getCourseNode())); } private void doDeleteNode(UserRequest ureq) { TreeNode tn = menuTree.getSelectedNode(); if (tn == null) { showError(NLS_DELETENODE_ERROR_SELECTFIRST); } else if (tn.getParent() == null) { showError(NLS_DELETENODE_ERROR_ROOTNODE); } else { // deletion is possible, start asking if really to delete. tabbedNodeConfig.setVisible(false); String title = translate("deletenode.header", tn.getTitle()); String message = translate("deletenode.confirm"); if(tn instanceof CourseEditorTreeNode && assessmentModeMgr.isNodeInUse(repoEntry, ((CourseEditorTreeNode)tn).getCourseNode())) { message = translate("deletenode.confirm.inuse.assessment.mode"); } else { message = translate("deletenode.confirm"); } deleteDialogController = activateYesNoDialog(ureq, title, message, deleteDialogController); } } private void doInsert(UserRequest ureq, CourseNode newNode) { menuTree.setSelectedNodeId(newNode.getIdent()); // update the current node in the editor course environment euce.getCourseEditorEnv().setCurrentCourseNodeId(newNode.getIdent()); euce.getCourseEditorEnv().validateCourse(); StatusDescription[] courseStatus = euce.getCourseEditorEnv().getCourseStatus(); updateCourseStatusMessages(getLocale(), courseStatus); initNodeEditor(ureq, newNode); // do logging ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_EDITOR_NODE_CREATED, getClass(), LoggingResourceable.wrap(newNode)); // Resize layout columns to make all nodes viewable in the menu column JSCommand resizeCommand = new JSCommand("try { OPOL.adjustHeight(); } catch(e) {if(window.console) console.log(e); }"); getWindowControl().getWindowBackOffice().sendCommandTo(resizeCommand); } private void doOpenNodeTypeChooser(UserRequest ureq) { removeAsListenerAndDispose(cmc); removeAsListenerAndDispose(chooseNodeTypeCtr); menuTree.getSelectedNode(); TreeNode tn = menuTree.getSelectedNode(); CourseEditorTreeNode cetn = tn == null ? null : cetm.getCourseEditorNodeById(tn.getIdent()); chooseNodeTypeCtr = new ChooseNodeController(ureq, getWindowControl(), ores, cetn); listenTo(chooseNodeTypeCtr); cmc = new CloseableModalController(getWindowControl(), translate("close"), chooseNodeTypeCtr.getInitialComponent(), true, translate("header.insertnodes")); listenTo(cmc); cmc.activate(); } private void doOpenStatusOverview(UserRequest ureq) { removeAsListenerAndDispose(statusCtr); statusCtr = new EditorStatusController(ureq, getWindowControl()); listenTo(statusCtr); euce.getCourseEditorEnv().validateCourse(); StatusDescription[] courseStatus = euce.getCourseEditorEnv().getCourseStatus(); statusCtr.updateStatus(cetm, courseStatus); calloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(), statusCtr.getInitialComponent(), statusLink, "", true, null); listenTo(calloutCtrl); calloutCtrl.activate(); } private void doStartHelpWizard(UserRequest ureq, Event event) { String findThis = event.getCommand().substring(NLS_START_HELP_WIZARD.length()); StatusDescription[] courseStatus = euce.getCourseEditorEnv().getCourseStatus(); for (int i = 0; i < courseStatus.length; i++) { String key = courseStatus[i].getDescriptionForUnit() + "." + courseStatus[i].getShortDescriptionKey(); if (key.equals(findThis)) { menuTree.setSelectedNodeId(courseStatus[i].getDescriptionForUnit()); euce.getCourseEditorEnv().setCurrentCourseNodeId(courseStatus[i].getDescriptionForUnit()); jumpToNodeEditor(courseStatus[i].getActivateableViewIdentifier(), ureq, cetm.getCourseNode(courseStatus[i].getDescriptionForUnit())); break; } } euce.getCourseEditorEnv().validateCourse(); courseStatus = euce.getCourseEditorEnv().getCourseStatus(); updateCourseStatusMessages(ureq.getLocale(), courseStatus); } private void dropNodeAsChild(UserRequest ureq, ICourse course, String droppedNodeId, String targetNodeId, boolean asChild, boolean atTheEnd) { menuTree.setDirty(true); // setDirty when moving CourseNode droppedNode = cetm.getCourseNode(droppedNodeId); int position; CourseEditorTreeNode insertParent; if(asChild) { insertParent = cetm.getCourseEditorNodeById(targetNodeId); position = atTheEnd ? -1 : 0; } else { CourseEditorTreeNode selectedNode = cetm.getCourseEditorNodeById(targetNodeId); if(selectedNode.getParent() == null) { //root node insertParent = selectedNode; position = 0; } else { insertParent = course.getEditorTreeModel().getCourseEditorNodeById(selectedNode.getParent().getIdent()); position = 0; for(position=insertParent.getChildCount(); position-->0; ) { if(insertParent.getChildAt(position).getIdent().equals(selectedNode.getIdent())) { position++; break; } } } } CourseEditorTreeNode moveFrom = course.getEditorTreeModel().getCourseEditorNodeById(droppedNode.getIdent()); //check if an ancestor is not dropped on a child if (course.getEditorTreeModel().checkIfIsChild(insertParent, moveFrom)) { showError("movecopynode.error.overlap"); fireEvent(ureq, Event.CANCELLED_EVENT); return; } //don't generate red screen for that. If the position is too high -> add the node at the end if(position >= insertParent.getChildCount()) { position = -1; } try { if(position >= 0) { insertParent.insert(moveFrom, position); } else { insertParent.addChild(moveFrom); } } catch (IndexOutOfBoundsException e) { logError("", e); //reattach the node as security, if not, the node is lost insertParent.addChild(moveFrom); } moveFrom.setDirty(true); //mark subtree as dirty TreeVisitor tv = new TreeVisitor( new Visitor() { public void visit(INode node) { CourseEditorTreeNode cetn = (CourseEditorTreeNode)node; cetn.setDirty(true); } }, moveFrom, true); tv.visitAll(); CourseFactory.saveCourseEditorTreeModel(course.getResourceableId()); showInfo("movecopynode.info.condmoved"); ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_EDITOR_NODE_MOVED, getClass()); euce.getCourseEditorEnv().validateCourse(); StatusDescription[] courseStatus = euce.getCourseEditorEnv().getCourseStatus(); updateCourseStatusMessages(ureq.getLocale(), courseStatus); } /** * @param ureq * @param courseStatus */ private void updateCourseStatusMessages(Locale locale, StatusDescription[] courseStatus) { List<String> errorIsForNode = new ArrayList<String>(); List<String> errorMessage = new ArrayList<String>(); List<String> errorHelpWizardLink = new ArrayList<String>(); List<String> warningIsForNode = new ArrayList<String>(); List<String> warningMessage = new ArrayList<String>(); List<String> warningHelpWizardLink = new ArrayList<String>(); // int errCnt = 0; int warCnt = 0; String helpWizardCmd; for (int i = 0; i < courseStatus.length; i++) { StatusDescription description = courseStatus[i]; String nodeId = courseStatus[i].getDescriptionForUnit(); String nodeName = cetm.getCourseNode(nodeId).getShortName(); // prepare wizard link helpWizardCmd = courseStatus[i].getActivateableViewIdentifier(); if (helpWizardCmd != null) { helpWizardCmd = "start.help.wizard" + courseStatus[i].getDescriptionForUnit() + "." + courseStatus[i].getShortDescriptionKey(); } else { helpWizardCmd = "NONE"; } if (description.isError()) { errCnt++; errorIsForNode.add(nodeName); errorMessage.add(description.getShortDescription(locale)); errorHelpWizardLink.add(helpWizardCmd); } else if (description.isWarning()) { warCnt++; warningIsForNode.add(nodeName); warningMessage.add(description.getShortDescription(locale)); warningHelpWizardLink.add(helpWizardCmd); } } if (errCnt > 0) { statusLink.setBadge(Integer.toString(errCnt), Level.error); } else if (warCnt > 0) { statusLink.setBadge(Integer.toString(warCnt), Level.warning); } else { statusLink.setBadge("\u2713", Level.success); } } private void launchPublishingWizard(UserRequest ureq, ICourse course, boolean requestOnClose) { if(publishStepsController != null) return;//ignore enter /* * start follwoing steps -> cancel wizardf does not touch data * (M) Mandatory (O) Optional * - (M)Step 00 -> show selection tree to choose changed nodes to be published * ...........-> calculate errors & warnings * ...........(next|finish) available if no errors or nothing to publish * - (O)Step 00A -> review errors & warnings * ...........(previous|next|finish) available * - (O)Step 00B -> review publish changes that will happen * ...........(previous|next|finish) available * - (O)Step 01 -> change general access to course * ...........(previous|finish) available * - FinishCallback -> apply course nodes change set * .................-> apply general access changes. */ Step start = new PublishStep00(ureq, cetm, course); /* * callback executed in case wizard is finished. */ StepRunnerCallback finish = new StepRunnerCallback(){ public Step execute(UserRequest ureq1, WindowControl wControl1, StepsRunContext runContext) { //all information to do now is within the runContext saved boolean hasChanges = false; PublishProcess publishManager = (PublishProcess)runContext.get("publishProcess"); PublishEvents publishEvents = publishManager.getPublishEvents(); if (runContext.containsKey("validPublish") && ((Boolean)runContext.get("validPublish")).booleanValue()) { @SuppressWarnings("unchecked") Collection<String> selectedNodeIds = (Collection<String>) runContext.get("publishSetCreatedFor"); hasChanges = (selectedNodeIds != null) && (selectedNodeIds.size() > 0); if (hasChanges) { publishManager.applyPublishSet(ureq1.getIdentity(), ureq1.getLocale(), false); } } if (runContext.containsKey("changedaccess")) { // there were changes made to the general course access String newAccessStr = (String) runContext.get("changedaccess"); int newAccess; boolean membersOnly = RepositoryEntry.MEMBERS_ONLY.equals(newAccessStr); if(membersOnly) { newAccess = RepositoryEntry.ACC_OWNERS; } else { newAccess = Integer.valueOf(newAccessStr); } // fires an EntryChangedEvent for repository entry notifying // about modification. publishManager.changeGeneralAccess(getIdentity(), newAccess, membersOnly); hasChanges = true; } if (runContext.containsKey("catalogChoice")) { String choice = (String) runContext.get("catalogChoice"); @SuppressWarnings("unchecked") List<CategoryLabel> categories = (List<CategoryLabel>)runContext.get("categories"); publishManager.publishToCatalog(choice, categories); } if(publishEvents.getPostPublishingEvents().size() > 0) { for(MultiUserEvent event:publishEvents.getPostPublishingEvents()) { CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(event, ores); } } // signal correct completion and tell if changes were made or not. return hasChanges ? StepsMainRunController.DONE_MODIFIED : StepsMainRunController.DONE_UNCHANGED; } }; 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()); } private void launchPreview(UserRequest ureq, ICourse course) { previewController = new PreviewConfigController(ureq, getWindowControl(), course); listenTo(previewController); stackPanel.pushController(translate("command.coursepreview"), previewController); } private void launchSinglePagesWizard(UserRequest ureq, ICourse course) { removeAsListenerAndDispose(multiSPChooserCtr); removeAsListenerAndDispose(cmc); VFSContainer rootContainer = course.getCourseEnvironment().getCourseFolderContainer(); CourseEditorTreeNode selectedNode = (CourseEditorTreeNode)menuTree.getSelectedNode(); multiSPChooserCtr = new MultiSPController(ureq, getWindowControl(), rootContainer, ores, selectedNode); listenTo(multiSPChooserCtr); cmc = new CloseableModalController(getWindowControl(), translate("close"), multiSPChooserCtr.getInitialComponent(), true, translate("multi.sps.title")); listenTo(cmc); cmc.activate(); } private void launchChecklistsWizard(UserRequest ureq) { removeAsListenerAndDispose(checklistWizard); Step start = new CheckList_1_CheckboxStep(ureq, ores); StepRunnerCallback finish = new CheckListStepRunnerCallback(ores); checklistWizard = new StepsMainRunController(ureq, getWindowControl(), start, finish, null, translate("checklist.wizard"), "o_sel_checklist_wizard", "Assessment#_checklist_multiple"); listenTo(checklistWizard); getWindowControl().pushAsModalDialog(checklistWizard.getInitialComponent()); } /** * @return true if lock on this course has been acquired, flase otherwhise */ public LockResult getLockEntry() { return lockEntry; } public boolean hasPublishableChanges(ICourse course) { if(cetm == null || course == null) { return false; } PublishProcess publishProcess = PublishProcess.getInstance(course, cetm, getLocale()); PublishTreeModel publishTreeModel = publishProcess.getPublishTreeModel(); return publishTreeModel.hasPublishableChanges(); } protected void doDispose() { ICourse course = CourseFactory.loadCourse(ores.getResourceableId()); CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, course); OLATResourceable lockEntryOres = OresHelper.createOLATResourceableInstance(LockEntry.class, 0l); CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, lockEntryOres); // those controllers are disposed by BasicController: nodeEditCntrllr = null; publishStepsController = null; deleteDialogController = null; cmc = null; moveCopyController = null; previewController = null; //toolC = null; columnLayoutCtr = null; moveCopyController = null; doReleaseEditLock(); ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_EDITOR_CLOSE, getClass()); } private void doReleaseEditLock() { if (lockEntry!=null && lockEntry.isSuccess()) { CoordinatorManager.getInstance().getCoordinator().getLocker().releaseLock(lockEntry); CourseFactory.fireModifyCourseEvent(ores.getResourceableId()); lockEntry = null; } } /** * @see org.olat.core.util.event.GenericEventListener#event(org.olat.core.gui.control.Event) */ @Override public void event(Event event) { try { if (event instanceof OLATResourceableJustBeforeDeletedEvent) { OLATResourceableJustBeforeDeletedEvent ojde = (OLATResourceableJustBeforeDeletedEvent) event; // make sure it is our course (actually not needed till now, since we // registered only to one event, but good style. if (ojde.targetEquals(ores, true)) { // true = throw an exception if the target does not match ores dispose(); } } else if (event instanceof LockRemovedEvent) { LockRemovedEvent lockEvent = (LockRemovedEvent)event; if(lockEntry != null && lockEntry.getLockEntry() != null && lockEntry.getLockEntry().equals(lockEvent.getLockEntry())) { this.dispose(); } } } catch (RuntimeException e) { log.warn(RELEASE_LOCK_AT_CATCH_EXCEPTION+" [in event(Event)]", e); dispose(); throw e; } } }