Newer
Older
/**
* 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.
*/
import java.util.Collections;
import java.util.Set;
import org.olat.core.CoreSpringFactory;
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.htmlsite.OlatCmdEvent;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.panel.Panel;
import org.olat.core.gui.components.stack.TooledStackedPanel;
import org.olat.core.gui.components.stack.TooledStackedPanel.Align;
import org.olat.core.gui.components.tree.GenericTreeModel;
import org.olat.core.gui.components.tree.MenuTree;
import org.olat.core.gui.components.tree.TreeEvent;
import org.olat.core.gui.components.tree.TreeModel;
import org.olat.core.gui.components.tree.TreeNode;
import org.olat.core.gui.components.velocity.VelocityContainer;

srosse
committed
import org.olat.core.gui.control.ConfigurationChangedListener;
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;

srosse
committed
import org.olat.core.gui.control.generic.dtabs.Activateable2;
import org.olat.core.gui.control.generic.messages.MessageController;
import org.olat.core.gui.control.generic.messages.MessageUIFactory;
import org.olat.core.gui.control.generic.textmarker.GlossaryMarkupItemController;

srosse
committed
import org.olat.core.gui.control.generic.title.TitledWrapperController;
import org.olat.core.id.Identity;
import org.olat.core.id.OLATResourceable;
import org.olat.core.id.context.ContextEntry;

srosse
committed
import org.olat.core.id.context.StateEntry;
import org.olat.core.logging.activity.CourseLoggingAction;
import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
import org.olat.core.util.coordinate.CoordinatorManager;
import org.olat.core.util.event.GenericEventListener;
import org.olat.core.util.prefs.Preferences;
import org.olat.core.util.resource.OLATResourceableJustBeforeDeletedEvent;
import org.olat.core.util.resource.OresHelper;
import org.olat.core.util.tree.TreeHelper;
import org.olat.course.CourseFactory;
import org.olat.course.CourseModule;
import org.olat.course.DisposedCourseRestartController;
import org.olat.course.ICourse;
import org.olat.course.assessment.AssessmentChangedEvent;
import org.olat.course.assessment.AssessmentMode;
import org.olat.course.assessment.manager.UserCourseInformationsManager;
import org.olat.course.config.CourseConfig;
import org.olat.course.editor.PublishEvent;
import org.olat.course.groupsandrights.CourseGroupManager;
import org.olat.course.nodes.CourseNode;
import org.olat.course.run.glossary.CourseGlossaryFactory;
import org.olat.course.run.glossary.CourseGlossaryToolLinkController;
import org.olat.course.run.navigation.NavigationHandler;
import org.olat.course.run.navigation.NodeClickedRef;

srosse
committed
import org.olat.course.run.userview.AssessmentModeTreeFilter;
import org.olat.course.run.userview.TreeFilter;
import org.olat.course.run.userview.UserCourseEnvironmentImpl;
import org.olat.course.run.userview.VisibleTreeFilter;
import org.olat.group.BusinessGroup;
import org.olat.group.ui.edit.BusinessGroupModifiedEvent;
import org.olat.modules.cp.TreeNodeEvent;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryManager;
import org.olat.repository.RepositoryService;
import org.olat.repository.model.RepositoryEntrySecurity;
import org.olat.util.logging.activity.LoggingResourceable;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Description: <br>
*
* @author Felix Jost
*/
public class RunMainController extends MainLayoutBasicController implements GenericEventListener, Activateable2 {
public static final String REBUILD = "rebuild";
public static final String ORES_TYPE_COURSE_RUN = OresHelper.calculateTypeName(RunMainController.class, CourseModule.ORES_TYPE_COURSE);
private final OLATResourceable courseRunOres; //course run ores for course run channel
private ICourse course;//o_clusterOK: this controller listen to course change events
private RepositoryEntry courseRepositoryEntry;
private Panel contentP;
private MenuTree luTree;
private VelocityContainer coursemain;
private UserCourseEnvironmentImpl uce;
private TooledStackedPanel toolbarPanel;
private LayoutMain3ColsController columnLayoutCtr;
private Controller currentNodeController; // the currently open node config
private boolean isInEditor = false;
private CourseNode currentCourseNode;
private TreeModel treeModel;

srosse
committed
private TreeFilter treeFilter;
private boolean needsRebuildAfter = false;
private boolean needsRebuildAfterPublish = false;
private boolean assessmentChangedEventReceived = false;
private Link nextLink, previousLink;
private GlossaryMarkupItemController glossaryMarkerCtr;
@Autowired
private RepositoryService repositoryService;
/**
* Constructor for the run main controller
*
* @param ureq The user request
* @param wControl The current window controller
* @param course The current course
* @param initialViewIdentifier if null the default view will be started,
* otherwise a controllerfactory type dependant view will be
* activated (subscription subtype) number: node with nodenumber will
* be activated "assessmentTool": assessment tool will be activated
* @param offerBookmark - whether to offer bookmarks or not
* @param showCourseConfigLink Flag to enable/disable link to detail-page in tool menu.
*/
public RunMainController(UserRequest ureq, WindowControl wControl, TooledStackedPanel toolbarPanel,
ICourse course, RepositoryEntry re, RepositoryEntrySecurity reSecurity, AssessmentMode assessmentMode) {
// Use repository package as fallback translator
super(ureq, wControl, Util.createPackageTranslator(RepositoryEntry.class, ureq.getLocale()));
this.toolbarPanel = toolbarPanel;
addLoggingResourceable(LoggingResourceable.wrap(course));
this.courseTitle = course.getCourseTitle();
this.courseRepositoryEntry = re;
this.courseRunOres = OresHelper.createOLATResourceableInstance(ORES_TYPE_COURSE_RUN, course.getResourceableId());
Identity identity = ureq.getIdentity();
// course and system logging
ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_ENTERING, getClass());
// log shows who entered which course, this can then be further used to jump
// to the courselog
logAudit("Entering course: [[["+courseTitle+"]]]", course.getResourceableId().toString());
luTree = new MenuTree(null, "luTreeRun", this);

srosse
committed
luTree.setScrollTopOnClick(true);
luTree.setElementCssClass("o_course_menu");
contentP = new Panel("building_block_content");

srosse
committed
// preload user assessment data in assessment properties cache to speed up
// course loading
course.getCourseEnvironment().getAssessmentManager().preloadCache(identity);
// build up the running structure for this user;
// get all group memberships for this course
uce = loadUserCourseEnvironment(ureq, reSecurity);
// build score now
uce.getScoreAccounting().evaluateAll();

srosse
committed
if(assessmentMode != null && assessmentMode.isRestrictAccessElements()) {

srosse
committed
treeFilter = new AssessmentModeTreeFilter(assessmentMode, uce.getCourseEnvironment().getRunStructure());

srosse
committed
} else {
treeFilter = new VisibleTreeFilter();
}
navHandler = new NavigationHandler(uce, treeFilter, false);
updateTreeAndContent(ureq, currentCourseNode, null);

srosse
committed
setLaunchDates();
if (courseRepositoryEntry != null && RepositoryManager.getInstance().createRepositoryEntryStatus(courseRepositoryEntry.getStatusCode()).isClosed()) {
wControl.setWarning(translate("course.closed"));
}
// add text marker wrapper controller to implement course glossary
// textMarkerCtr must be created before the toolC!
CourseConfig cc = uce.getCourseEnvironment().getCourseConfig();
glossaryMarkerCtr = CourseGlossaryFactory.createGlossaryMarkupWrapper(ureq, wControl, contentP, cc);
if (glossaryMarkerCtr != null) {
listenTo(glossaryMarkerCtr);
// enable / disable glossary highlighting according to user prefs
Preferences prefs = ureq.getUserSession().getGuiPreferences();
Boolean state = (Boolean) prefs.get(CourseGlossaryToolLinkController.class, CourseGlossaryFactory.createGuiPrefsKey(course));
//Glossary always on for guests. OLAT-4241
if(ureq.getUserSession().getRoles().isGuestOnly()){
state = Boolean.TRUE;
}
if (state == null) {
glossaryMarkerCtr.setTextMarkingEnabled(false);
} else {
glossaryMarkerCtr.setTextMarkingEnabled(state.booleanValue());
}
columnLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), luTree, glossaryMarkerCtr.getInitialComponent(), "course" + course.getResourceableId());
columnLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), luTree, contentP, "courseRun" + course.getResourceableId());
}
listenTo(columnLayoutCtr);
// activate the custom course css if any
setCustomCSS(CourseFactory.getCustomCourseCss(ureq.getUserSession(), uce.getCourseEnvironment()));
coursemain = createVelocityContainer("index");
coursemain.setDomReplaceable(false);
// see function gotonode in functions.js to see why we need the repositoryentry-key here:
// it is to correctly apply external links using course-internal links via javascript
coursemain.contextPut("courserepokey", courseRepositoryEntry.getKey());
coursemain.put("coursemain", columnLayoutCtr.getInitialComponent());
// disposed message controller must be created beforehand
Controller courseCloser = new DisposedCourseRestartController(ureq, getWindowControl(), courseRepositoryEntry);
Controller disposedRestartController = new LayoutMain3ColsController(ureq, wControl, courseCloser);
setDisposedMsgController(disposedRestartController);
// add as listener to course so we are being notified about course events:
// - publish changes
// - assessment events
// - listen for CourseConfig events
CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, identity, course);
// - group modification events
CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, identity, courseRepositoryEntry);
}
protected void setTextMarkingEnabled(boolean enabled) {
if (glossaryMarkerCtr != null) {
glossaryMarkerCtr.setTextMarkingEnabled(enabled);
}
}
protected void initToolbar() {
if(toolbarPanel != null) {
// new toolbox 'general'
previousLink = LinkFactory.createToolLink("previouselement","", this, "o_icon_previous_toolbar");
previousLink.setTitle(translate("command.previous"));
toolbarPanel.addTool(previousLink, Align.rightEdge, false, "o_tool_previous");
nextLink = LinkFactory.createToolLink("nextelement","", this, "o_icon_next_toolbar");
nextLink.setTitle(translate("command.next"));
toolbarPanel.addTool(nextLink, Align.rightEdge, false, "o_tool_next");
updateNextPrevious();
private UserCourseEnvironmentImpl loadUserCourseEnvironment(UserRequest ureq, RepositoryEntrySecurity reSecurity) {
CourseGroupManager cgm = course.getCourseEnvironment().getCourseGroupManager();
List<BusinessGroup> coachedGroups;
if(reSecurity.isGroupCoach()) {
coachedGroups = cgm.getOwnedBusinessGroups(ureq.getIdentity());
} else {
coachedGroups = Collections.emptyList();
}
List<BusinessGroup> participatedGroups;
if(reSecurity.isGroupParticipant()) {
participatedGroups = cgm.getParticipatingBusinessGroups(ureq.getIdentity());
} else {
participatedGroups = Collections.emptyList();
}
List<BusinessGroup> waitingLists;
if(reSecurity.isGroupWaiting()) {
waitingLists = cgm.getWaitingListGroups(ureq.getIdentity());
} else {
waitingLists = Collections.emptyList();
}
return new UserCourseEnvironmentImpl(ureq.getUserSession().getIdentityEnvironment(), course.getCourseEnvironment(),
coachedGroups, participatedGroups, waitingLists,
reSecurity.isCourseCoach() || reSecurity.isGroupCoach(),
reSecurity.isEntryAdmin(),
reSecurity.isCourseParticipant() || reSecurity.isGroupParticipant());
* Initialize the users group memberships for groups used within this course
*
* @param identity
protected void reloadGroupMemberships(RepositoryEntrySecurity reSecurity) {
CourseGroupManager cgm = course.getCourseEnvironment().getCourseGroupManager();
List<BusinessGroup> coachedGroups;
if(reSecurity.isGroupCoach()) {
coachedGroups = cgm.getOwnedBusinessGroups(getIdentity());
} else {
coachedGroups = Collections.emptyList();
}
List<BusinessGroup> participatedGroups;
if(reSecurity.isGroupParticipant()) {
participatedGroups = cgm.getParticipatingBusinessGroups(getIdentity());
} else {
participatedGroups = Collections.emptyList();
}
List<BusinessGroup> waitingLists;
if(reSecurity.isGroupWaiting()) {
waitingLists = cgm.getWaitingListGroups(getIdentity());
} else {
waitingLists = Collections.emptyList();
}
uce.setGroupMemberships(coachedGroups, participatedGroups, waitingLists);
needsRebuildAfterRunDone = true;
}
private void setLaunchDates() {
UserCourseInformationsManager userCourseInfoMgr = CoreSpringFactory.getImpl(UserCourseInformationsManager.class);
userCourseInfoMgr.updateUserCourseInformations(uce.getCourseEnvironment().getCourseGroupManager().getCourseResource(), getIdentity());

srosse
committed
private CourseNode updateAfterChanges(CourseNode courseNode) {
if(currentCourseNode == null) return null;

srosse
committed
CourseNode newCurrentCourseNode;
NodeClickedRef nclr = navHandler.reloadTreeAfterChanges(courseNode);
if(nclr == null) {
doDisposeAfterEvent();
newCurrentCourseNode = null;
} else {
treeModel = nclr.getTreeModel();
luTree.setTreeModel(treeModel);
String selNodeId = nclr.getSelectedNodeId();
luTree.setSelectedNodeId(selNodeId);
luTree.setOpenNodeIds(nclr.getOpenNodeIds());
newCurrentCourseNode = nclr.getCalledCourseNode();
}
return newCurrentCourseNode;
protected void updateNextPrevious() {
if(nextLink == null || previousLink == null || luTree == null) {
return;
}
boolean hasPrevious;
boolean hasNext;
if(luTree.getSelectedNode() == null) {
hasPrevious = true;
hasNext = true;
} else {
List<TreeNode> flatTree = new ArrayList<>();
TreeHelper.makeTreeFlat(luTree.getTreeModel().getRootNode(), flatTree);
int index = flatTree.indexOf(luTree.getSelectedNode());
hasPrevious = index > 0;
hasNext = index >= 0 && index+1 < flatTree.size();
}
previousLink.setEnabled(hasPrevious);
nextLink.setEnabled(hasNext);
}
/**
* side-effecty to content and luTree
*
* @param ureq
* @param calledCourseNode the node to jump to, if null = jump to root node
* @param nodecmd An optional command used to activate the run view or NULL if not available
* @return true if the node jumped to is visible
*/
private boolean updateTreeAndContent(UserRequest ureq, CourseNode calledCourseNode, String nodecmd) {

srosse
committed
return updateTreeAndContent(ureq, calledCourseNode, nodecmd, null, null);
}
private boolean updateTreeAndContent(UserRequest ureq, CourseNode calledCourseNode, String nodecmd, List<ContextEntry> entries, StateEntry state) {
// build menu (treemodel)
// dispose old node controller before creating the NodeClickedRef which creates
// the new node controller. It is important that the old node controller is
// disposed before the new one to not get conflicts with cacheable mappers that
// might be used in both controllers with the same ID (e.g. the course folder)
if (currentNodeController != null && !currentNodeController.isDisposed() && !navHandler.isListening(currentNodeController)) {
currentNodeController.dispose();
}
NodeClickedRef nclr = navHandler.evaluateJumpToCourseNode(ureq, getWindowControl(), calledCourseNode, this, nodecmd);
if (!nclr.isVisible()) {
// if not root -> fallback to root. e.g. when a direct node jump fails
if (calledCourseNode != null) {

srosse
committed
nclr = navHandler.evaluateJumpToCourseNode(ureq, getWindowControl(), null, this, null);
MessageController msgController = MessageUIFactory.createInfoMessage(ureq, getWindowControl(), translate("course.noaccess.title"), translate("course.noaccess.text"));
contentP.setContent(msgController.getInitialComponent());
luTree.setTreeModel(new GenericTreeModel());
return false;
}
}
treeModel = nclr.getTreeModel();
luTree.setTreeModel(treeModel);
String selNodeId = nclr.getSelectedNodeId();
luTree.setSelectedNodeId(selNodeId);
// get new run controller.
currentNodeController = nclr.getRunController();
addToHistory(ureq, currentNodeController);
if (currentNodeController instanceof TitledWrapperController) {

srosse
committed
Controller contentcontroller = ((TitledWrapperController)currentNodeController).getContentController();
addToHistory(ureq, contentcontroller);

srosse
committed
if(contentcontroller instanceof Activateable2) {
((Activateable2)contentcontroller).activate(ureq, entries, state);
}
} else if(currentNodeController instanceof Activateable2) {
((Activateable2)currentNodeController).activate(ureq, entries, state);
} else if(currentNodeController != null) {
contentP.setContent(currentNodeController.getInitialComponent());
} else {
MessageController msgCtrl = MessageUIFactory
.createWarnMessage(ureq, getWindowControl(), null, translate("msg.nodenotavailableanymore"));
listenTo(msgCtrl);
contentP.setContent(msgCtrl.getInitialComponent());

srosse
committed
}
updateLastUsage(nclr.getCalledCourseNode());
private void updateLastUsage(CourseNode calledCourseNode) {
if(calledCourseNode != null && calledCourseNode.needsReferenceToARepositoryEntry()) {
RepositoryEntry referencedRe = calledCourseNode.getReferencedRepositoryEntry();
if(referencedRe != null) {
repositoryService.setLastUsageNowFor(referencedRe);
}
}
}
/**
* @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) {
if(needsRebuildAfter) {

srosse
committed
currentCourseNode = updateAfterChanges(currentCourseNode);
needsRebuildAfter = false;
}

gnaegi
committed
// Links in editTools dropdown
if(nextLink == source) {
doNext(ureq);
} else if(previousLink == source) {
doPrevious(ureq);
} else if (source == luTree) {
if (event.getCommand().equals(MenuTree.COMMAND_TREENODE_CLICKED)) {
TreeEvent tev = (TreeEvent) event;
doNodeClick(ureq, tev);
}
} else if (source == coursemain) {
if (event.getCommand().equals("activateCourseNode")) {
// Events from the JS function o_activateCourseNode() - activate the given node id
String nodeid = ureq.getParameter("nodeid");
if (nodeid != null) {
CourseNode identNode = course.getRunStructure().getNode(nodeid);
boolean success = updateTreeAndContent(ureq, identNode, null);
if (success) {
currentCourseNode = identNode;
} else {
getWindowControl().setWarning(translate("msg.nodenotavailableanymore"));
}
protected void toolCtrDone(UserRequest ureq) {
if (isInEditor) {
isInEditor = false; // for clarity
if (needsRebuildAfterPublish) {
needsRebuildAfterPublish = false;
// rebuild up the running structure for this user, after publish;
course = CourseFactory.loadCourse(course.getResourceableId());
uce = new UserCourseEnvironmentImpl(ureq.getUserSession().getIdentityEnvironment(), course.getCourseEnvironment(),
uce.getCoachedGroups(), uce.getParticipatingGroups(), uce.getWaitingLists(),
null, null, null);
// build score now
uce.getScoreAccounting().evaluateAll();

srosse
committed
navHandler = new NavigationHandler(uce, treeFilter, false);
// rebuild and jump to root node
updateTreeAndContent(ureq, null, null);
}
}
}
protected boolean isInEditor() {
return isInEditor;
}
protected void setInEditor(boolean isInEditor) {
this.isInEditor = isInEditor;
}
/**
* @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)
*/
public void event(UserRequest ureq, Controller source, Event event) {
if(needsRebuildAfter) {

srosse
committed
currentCourseNode = updateAfterChanges(currentCourseNode);
needsRebuildAfter = false;
}
// event from the current tool (editor, groupmanagement, archiver)
if (source == currentNodeController) {
if (event instanceof OlatCmdEvent) {
OlatCmdEvent oe = (OlatCmdEvent) event;
String cmd = oe.getCommand();
if (cmd.equals(OlatCmdEvent.GOTONODE_CMD)) {
String subcmd = oe.getSubcommand(); // "69680861018558/node-specific-data";
CourseNode identNode;
String nodecmd = null;
int slPos = subcmd.indexOf('/');
if (slPos != -1) {
nodecmd = subcmd.substring(slPos + 1);
identNode = course.getRunStructure().getNode(subcmd.substring(0, slPos));
} else {
identNode = course.getRunStructure().getNode(subcmd);
}
addLoggingResourceable(LoggingResourceable.wrap(identNode));
currentCourseNode = identNode;
updateTreeAndContent(ureq, identNode, nodecmd);
oe.accept();
}
} else if (event == Event.DONE_EVENT) {
// the controller is done.
// we have a chance here to test if we need to refresh the evalution.
// this is the case when a test was submitted and scoring has changed ->
// preconditions may have changed
if (needsRebuildAfterRunDone) {
needsRebuildAfterRunDone = false;
updateTreeAndContent(ureq, currentCourseNode, null);
}
} else if (REBUILD.equals(event.getCommand())) {
needsRebuildAfterRunDone = false;
updateTreeAndContent(ureq, currentCourseNode, null);
} else if (event instanceof TreeNodeEvent) {
TreeNodeEvent tne = (TreeNodeEvent) event;
TreeNode newCpTreeNode = tne.getChosenTreeNode();
luTree.setSelectedNodeId(newCpTreeNode.getIdent());
} else if (event == Event.CHANGED_EVENT) {
updateTreeAndContent(ureq, currentCourseNode, null);
} else if (event instanceof BusinessGroupModifiedEvent) {
updateTreeAndContent(ureq, currentCourseNode, null);
private void doNext(UserRequest ureq) {
List<TreeNode> flatList = new ArrayList<>();
TreeNode currentNode = luTree.getSelectedNode();
TreeHelper.makeTreeFlat(luTree.getTreeModel().getRootNode(), flatList);
int index = flatList.indexOf(currentNode);
if(index >= 0 && index+1 <flatList.size()) {
TreeNode nextNode = flatList.get(index + 1);
TreeEvent tev = new TreeEvent(MenuTree.COMMAND_TREENODE_CLICKED, nextNode.getIdent());
doNodeClick(ureq, tev);
}
}
private void doPrevious(UserRequest ureq) {
List<TreeNode> flatList = new ArrayList<>();
TreeNode currentNode = luTree.getSelectedNode();
TreeHelper.makeTreeFlat(luTree.getTreeModel().getRootNode(), flatList);
int index = flatList.indexOf(currentNode);
if(index-1 >= 0 && index-1 < flatList.size()) {
TreeNode previousNode = flatList.get(index - 1);
TreeEvent tev = new TreeEvent(MenuTree.COMMAND_TREENODE_CLICKED, previousNode.getIdent());
doNodeClick(ureq, tev);
}
}
private void doNodeClick(UserRequest ureq, TreeEvent tev) {
if(assessmentChangedEventReceived) {
uce.getScoreAccounting().evaluateAll();
assessmentChangedEventReceived = false;
}
// goto node:
// after a click in the tree, evaluate the model anew, and set the
// selection of the tree again
NodeClickedRef nclr = navHandler.evaluateJumpToTreeNode(ureq, getWindowControl(), treeModel, tev, this, null, currentNodeController);
if (!nclr.isVisible()) {
getWindowControl().setWarning(translate("msg.nodenotavailableanymore"));
// go to root since the current node is no more visible
updateTreeAndContent(ureq, null, null);
updateNextPrevious();
return;
}
// a click to a subtree's node
if (nclr.isHandledBySubTreeModelListener() || nclr.getSelectedNodeId() == null) {
if(nclr.getRunController() != null) {
//there is an update to the currentNodeController, apply it
if (currentNodeController != null && !currentNodeController.isDisposed() && !navHandler.isListening(currentNodeController)) {
currentNodeController.dispose();
}
currentNodeController = nclr.getRunController();
Component nodeComp = currentNodeController.getInitialComponent();
contentP.setContent(nodeComp);
updateLastUsage(nclr.getCalledCourseNode());

srosse
committed
if(nclr.getSelectedNodeId() != null && nclr.getOpenNodeIds() != null) {
luTree.setSelectedNodeId(nclr.getSelectedNodeId());
luTree.setOpenNodeIds(nclr.getOpenNodeIds());
}
return;
}
// set the new treemodel
treeModel = nclr.getTreeModel();
luTree.setTreeModel(treeModel);
// set the new tree selection
String nodeId = nclr.getSelectedNodeId();
luTree.setSelectedNodeId(nodeId);
luTree.setOpenNodeIds(nclr.getOpenNodeIds());
currentCourseNode = nclr.getCalledCourseNode();
// get new run controller. Dispose only if not already disposed in navHandler.evaluateJumpToTreeNode()
if (currentNodeController != null && !currentNodeController.isDisposed() && !navHandler.isListening(currentNodeController)) {
currentNodeController.dispose();
}
currentNodeController = nclr.getRunController();
updateLastUsage(nclr.getCalledCourseNode());
Component nodeComp = currentNodeController.getInitialComponent();
contentP.setContent(nodeComp);
addToHistory(ureq, currentNodeController);
// set glossary wrapper dirty after menu click to make it reload the glossary
// stuff properly when in AJAX mode
if (glossaryMarkerCtr != null && glossaryMarkerCtr.isTextMarkingEnabled()) {
glossaryMarkerCtr.getInitialComponent().setDirty(true);
}
/**
* implementation of listener which listens to publish events
*
* @see org.olat.core.util.event.GenericEventListener#event(org.olat.core.gui.control.Event)
*/
if (event instanceof PublishEvent) {
PublishEvent pe = (PublishEvent)event;
if(course.getResourceableId().equals(pe.getPublishedCourseResId())) {
processPublishEvent(pe);
}
} else 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(course, true)) {
doDisposeAfterEvent();
}
} else if (event instanceof AssessmentChangedEvent) {
AssessmentChangedEvent ace = (AssessmentChangedEvent) event;
Identity identity = uce.getIdentityEnvironment().getIdentity();
// reevaluate the changed node if the event changed the current user
if (ace.getIdentityKey().equals(identity.getKey())) {
String assessmentChangeType = ace.getCommand();
// do not re-evaluate things if only comment has been changed
if (assessmentChangeType.equals(AssessmentChangedEvent.TYPE_SCORE_EVAL_CHANGED)
|| assessmentChangeType.equals(AssessmentChangedEvent.TYPE_ATTEMPTS_CHANGED)) {
//LD: do not recalculate the score now, but at the next click, since the event comes before DB commit
//uce.getScoreAccounting().evaluateAll();
assessmentChangedEventReceived = true;
}
// raise a flag to indicate refresh
needsRebuildAfterRunDone = true;
}
}
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
private void processPublishEvent(PublishEvent pe) {
if (pe.getState() == PublishEvent.PRE_PUBLISH) {
// so far not interested in PRE PUBLISH event, but one could add user
// and the currently active BB information. This in turn could be used
// by the publish event issuer to decide whether or not to publish...
} else if (pe.getState() == PublishEvent.PUBLISH) {
// disable this controller and issue a information
if (isInEditor) {
needsRebuildAfterPublish = true;
// author went in editor and published the course -> raise a flag to
// later prepare the new
// course to present him/her a nice view when
// he/she closes the editor to return to the run main (this controller)
} else if(getIdentity().getKey().equals(pe.getAuthorKey())) {
//do nothing
} else {
if(currentCourseNode == null) {
needsRebuildAfter = true;
} else {
try {
String currentNodeIdent = currentCourseNode.getIdent();
Set<String> deletedNodeIds = pe.getDeletedCourseNodeIds();
Set<String> modifiedNodeIds = pe.getModifiedCourseNodeIds();
if(deletedNodeIds != null && deletedNodeIds.contains(currentNodeIdent)) {
doDisposeAfterEvent();
} else if(modifiedNodeIds != null && modifiedNodeIds.contains(currentNodeIdent)) {
doDisposeAfterEvent();
//needsRebuildAfter = true;

srosse
committed
} else {
needsRebuildAfter = true;
}
} catch (Exception e) {
logError("", e);
//beyond update, be paranoid
doDisposeAfterEvent();
}
}
}
}
}
protected void doDisposeAfterEvent() {
if(currentNodeController instanceof ConfigurationChangedListener) {
//give to opportunity to close popups ...
((ConfigurationChangedListener)currentNodeController).configurationChanged();
}
}
UserCourseEnvironmentImpl getUce() {
return uce;
}
CourseNode getCurrentCourseNode() {
return currentCourseNode;
}
/**
* @see org.olat.core.gui.control.DefaultController#doDispose(boolean)
*/
protected void doDispose() {
// remove as listener from this course events:
// - group modification events
CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, courseRepositoryEntry);
// - publish changes
// - assessment events
CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, course);
//remove as listener from course run eventAgency
CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, courseRunOres);
// currentNodeController must be disposed manually does not work with
// general BasicController methods
if (currentNodeController != null && !currentNodeController.isDisposed()) {
currentNodeController = null;
navHandler.dispose();
// log to Statistical and User log
ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_LEAVING, getClass());
// log the fact that the user is leaving the course in the log file
logAudit("Leaving course: [[["+courseTitle+"]]]", course.getResourceableId().toString());
}

srosse
committed
@Override
public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
if(entries == null || entries.isEmpty()) return;
ContextEntry firstEntry = entries.get(0);
String type = firstEntry.getOLATResourceable().getResourceableTypeName();
if("CourseNode".equalsIgnoreCase(type)) {

srosse
committed
CourseNode cn = course.getRunStructure().getNode(firstEntry.getOLATResourceable().getResourceableId().toString());
if(currentCourseNode == null || !currentCourseNode.equals(cn)) {
getWindowControl().makeFlat();
// add logging information for case course gets started via jump-in
// link/search
addLoggingResourceable(LoggingResourceable.wrap(course));
if (cn != null) {
addLoggingResourceable(LoggingResourceable.wrap(cn));
}
if(entries.size() > 1) {
entries = entries.subList(1, entries.size());
}
updateTreeAndContent(ureq, cn, null, entries, firstEntry.getTransientState());

srosse
committed
}
}
}
public void disableToolController(boolean disable) {
toolbarPanel.setToolbarEnabled(!disable);

srosse
committed
}

gnaegi
committed
public void disableCourseClose(boolean disable) {
toolbarPanel.setShowCloseLink(true, !disable);
}

srosse
committed
@Override
public void setDisposedMsgController(Controller disposeMsgController) {
super.setDisposedMsgController(disposeMsgController);
}