Package org.olat.course.run

Source Code of org.olat.course.run.RunMainController

/**
* 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.
* <p>
*/

package org.olat.course.run;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.olat.basesecurity.Manager;
import org.olat.basesecurity.ManagerFactory;
import org.olat.bookmark.AddAndEditBookmarkController;
import org.olat.bookmark.BookmarkManager;
import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory;
import org.olat.core.commons.persistence.PersistenceHelper;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.Windows;
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.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;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.controller.MainLayoutBasicController;
import org.olat.core.gui.control.creator.ControllerCreator;
import org.olat.core.gui.control.generic.dtabs.Activateable;
import org.olat.core.gui.control.generic.dtabs.DTabs;
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.popup.PopupBrowserWindow;
import org.olat.core.gui.control.generic.textmarker.GlossaryMarkupItemController;
import org.olat.core.gui.control.generic.tool.ToolController;
import org.olat.core.gui.control.generic.tool.ToolFactory;
import org.olat.core.gui.control.state.ControllerState;
import org.olat.core.gui.translator.PackageTranslator;
import org.olat.core.id.Identity;
import org.olat.core.id.OLATResourceable;
import org.olat.core.id.context.BusinessControl;
import org.olat.core.id.context.BusinessControlFactory;
import org.olat.core.id.context.ContextEntry;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.OLATSecurityException;
import org.olat.core.logging.Tracing;
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.event.MultiUserEvent;
import org.olat.core.util.prefs.Preferences;
import org.olat.core.util.resource.OLATResourceableJustBeforeDeletedEvent;
import org.olat.core.util.resource.OresHelper;
import org.olat.course.CourseFactory;
import org.olat.course.CourseModule;
import org.olat.course.ICourse;
import org.olat.course.archiver.ArchiverMainController;
import org.olat.course.archiver.IArchiverCallback;
import org.olat.course.assessment.AssessmentChangedEvent;
import org.olat.course.assessment.AssessmentMainController;
import org.olat.course.assessment.CoachingGroupAccessAssessmentCallback;
import org.olat.course.assessment.EfficiencyStatement;
import org.olat.course.assessment.EfficiencyStatementController;
import org.olat.course.assessment.EfficiencyStatementManager;
import org.olat.course.assessment.FullAccessAssessmentCallback;
import org.olat.course.config.CourseConfig;
import org.olat.course.config.CourseConfigEvent;
import org.olat.course.editor.PublishEvent;
import org.olat.course.groupsandrights.CourseGroupManager;
import org.olat.course.groupsandrights.CourseRights;
import org.olat.course.groupsandrights.ui.CourseGroupManagementMainController;
import org.olat.course.nodes.CourseNode;
import org.olat.course.run.calendar.CourseCalendarController;
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;
import org.olat.course.run.userview.UserCourseEnvironment;
import org.olat.course.run.userview.UserCourseEnvironmentImpl;
import org.olat.course.statistic.StatisticMainController;
import org.olat.group.BusinessGroup;
import org.olat.group.BusinessGroupManager;
import org.olat.group.BusinessGroupManagerImpl;
import org.olat.group.ui.BGControllerFactory;
import org.olat.group.ui.edit.BusinessGroupModifiedEvent;
import org.olat.instantMessaging.InstantMessagingModule;
import org.olat.instantMessaging.groupchat.GroupChatManagerController;
import org.olat.modules.cp.TreeNodeEvent;
import org.olat.note.NoteController;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryEntryStatus;
import org.olat.repository.RepositoryManager;
import org.olat.repository.controllers.EntryChangedEvent;
import org.olat.repository.controllers.RepositoryDetailsController;
import org.olat.repository.controllers.RepositoryMainController;
import org.olat.repository.site.RepositorySite;
import org.olat.util.logging.activity.LoggingResourceable;

/**
* Description: <br>
*
* @author Felix Jost
*/
public class RunMainController extends MainLayoutBasicController implements GenericEventListener, Activateable {
  private static final String COMMAND_EDIT = "gotoeditor";
  private static final String TOOLBOX_LINK_COURSECONFIG = "courseconfig";

  private static final String LOG_ENTERING = "ENTERING";
  private static final String LOG_LEAVING = "LEAVING";

  private static final String CMD_START_GROUP_PREFIX = "cmd.group.start.ident.";

  private static final String ACTION_CALENDAR = "cal";
  private static final String ACTION_BOOKMARK = "bm";
  private static final String TOOL_BOOKMARK = "b";
 
  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 MenuTree luTree;
  private Panel contentP;
  private Panel all;

  private NavigationHandler navHandler;
  private UserCourseEnvironment uce;
  private LayoutMain3ColsController columnLayoutCtr;
  private GroupChatManagerController courseChatManagerCtr;

  private Controller currentToolCtr;
  private ToolController toolC;
  private Controller currentNodeController; // the currently open node config
  private AddAndEditBookmarkController bookmarkController;

  private boolean isInEditor = false;

  private Map courseRightsCache = new HashMap();
  private boolean isCourseAdmin = false;
  private boolean isCourseCoach = false;
  private List ownedGroups;
  private List participatedGroups;
  private List waitingListGroups;
  private List rightGroups;

  private CourseNode currentCourseNode;
  private TreeModel treeModel;
  private boolean needsRebuildAfterPublish;
  private boolean needsRebuildAfterRunDone = false;
 
  private String courseTitle;
  private boolean isGuest = true;
  private boolean offerBookmark = true;
 
  private GlossaryMarkupItemController glossaryMarkerCtr;
  private CourseGlossaryToolLinkController glossaryToolCtr;
  private boolean showCourseConfigLink;
  private VelocityContainer coursemain;
 
  private boolean assessmentChangedEventReceived = false;
  private Link currentUserCount;
 

  /**
   * 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(final UserRequest ureq, final WindowControl wControl, final ICourse course, final String initialViewIdentifier,
      final boolean offerBookmark, final boolean showCourseConfigLink) {
    super(ureq, wControl);   
       
    this.course = course;
    addLoggingResourceable(LoggingResourceable.wrap(course));
    this.courseTitle = course.getCourseTitle();
    this.courseRepositoryEntry = RepositoryManager.getInstance().lookupRepositoryEntry(course, true);
    this.offerBookmark = offerBookmark;
    this.showCourseConfigLink = showCourseConfigLink;
    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
    Tracing.logAudit("Entering course: [[["+courseTitle+"]]]", course.getResourceableId().toString(), RunMainController.class);
   
    // set up the components
    all = new Panel("allofcourse");
    luTree = new MenuTree("luTreeRun", this);
    contentP = new Panel("building_block_content");

    // get all group memberships for this course
    initGroupMemberships(identity);
    // Initialize the the users roles and right for this course
    // 1) is guest flag
    isGuest = ureq.getUserSession().getRoles().isGuestOnly();
    // 2) all course internal rights
    initUserRolesAndRights(identity);

    // preload user assessment data in assessmnt properties cache to speed up
    // course loading
    course.getCourseEnvironment().getAssessmentManager().preloadCache(identity);

    // build up the running structure for this user;
    uce = new UserCourseEnvironmentImpl(ureq.getUserSession().getIdentityEnvironment(), course.getCourseEnvironment());
    // build score now
    uce.getScoreAccounting().evaluateAll();
    navHandler = new NavigationHandler(uce, false);

    // jump to either the forum or the folder if the business-launch-path says
    // so.
    boolean showAssessmentTool = false;
   
    BusinessControl bc = getWindowControl().getBusinessControl();
    ContextEntry ce = bc.popLauncherContextEntry();
    if (ce != null) {
      logDebug("businesscontrol (for further jumps) would be:" + bc, null);
      OLATResourceable ores = ce.getOLATResourceable();
      logDebug("OLATResourceable=" + ores, null);
      if (OresHelper.isOfType(ores, CourseNode.class)) {
        // TODO, don't use CourseNode.class, but a lookup using the ClassToId
        // class
        // jump to the coursenode with id
        Long nodeId = ores.getResourceableId();
        String nodeIdS = nodeId.toString();
        currentCourseNode = course.getRunStructure().getNode(nodeIdS);
        if (currentCourseNode == null) {
          // (e.g. a coursenode that no longer is accessible or existing)
          // -> fallback to root node (automatically done later), and drop wrong
          // remaining contexts
          logDebug("currentCourseNode=null => dropLauncherEntries for nodeIdS=" + nodeIdS, null);
          bc.dropLauncherEntries();
        }
      } else if ("assessmentTool".equals(ores.getResourceableTypeName())) {
        showAssessmentTool = true;
      }
    }
    /*
     * ContextEntry curCe = bc.getCurrentEntryAndAdvance(); if (curCe != null) { //
     * e.g something like FOCourseNode:3243 or AssementTool:0 or AssmentTool:1
     * (0,1 must be dokumented) OLATResourceable ores =
     * curCe.getOLATResourceable(); if (OresHelper.isOfType(ores,
     * CourseNode.class)) { // TODO, don't use CourseNode.class, but a lookup
     * using the ClassToId class // jump to the coursenode with id Long nodeId =
     * ores.getResourceableId(); String nodeIdS = nodeId.toString();
     * currentCourseNode = course.getRunStructure().getNode(nodeIdS); } else {
     * //TODO assessment tool } }
     */
   
   
    String subsubId = null;
    String assessmentViewIdentifier = null;
    // activate assessent tool or node as the first screen
    if (initialViewIdentifier != null) {
      if (initialViewIdentifier.equals("assessmentTool")) {
        showAssessmentTool = true;
        // assessmentViewIdentifier must be null
      } else if (initialViewIdentifier.equals("assessmentTool:nodeChoose")) {
        showAssessmentTool = true;
        assessmentViewIdentifier = "node-choose";
      } else {
        // check for subsubIdent
        if (initialViewIdentifier.indexOf(":") != -1) {
          String[] viewIdentifier = initialViewIdentifier.split(":");
          currentCourseNode = course.getRunStructure().getNode(viewIdentifier[0]);
          subsubId = viewIdentifier[1];
        } else {
          currentCourseNode = course.getRunStructure().getNode(initialViewIdentifier);
        }
      }
    }

    updateTreeAndContent(ureq, currentCourseNode, subsubId);

    if (courseRepositoryEntry != null && RepositoryManager.getInstance().createRepositoryEntryStatus(courseRepositoryEntry.getStatusCode()).isClosed()) {
      wControl.setWarning(translate("course.closed"));
    }
   
    // instant messaging: send presence like "reading course: Biology I" as
    // awareness info. Important: do this before initializing the toolbox
    // controller!
    if (InstantMessagingModule.isEnabled() && CourseModule.isCourseChatEnabled() && !isGuest) {
      String intro = translate("course.presence.message.enter") + " " + getExtendedCourseTitle(ureq.getLocale());
      InstantMessagingModule.getAdapter().sendStatus(identity.getName(), intro);

      if (course.getCourseEnvironment().getCourseConfig().isChatEnabled()) {
        // create groupchat room link only if course chat is enabled
        createCourseGroupChatLink(ureq);
      }
    }

    // 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);
 
    boolean hasGlossaryRights = hasCourseRight(CourseRights.RIGHT_GLOSSARY) || isCourseAdmin;
    glossaryToolCtr = new CourseGlossaryToolLinkController(getWindowControl(), ureq, course, getTranslator(), hasGlossaryRights,
        uce.getCourseEnvironment(), glossaryMarkerCtr);
    listenTo(glossaryToolCtr);
    // init the menu and tool controller
    toolC = initToolController(identity, ureq);
    Component toolComp = (toolC == null ? null : toolC.getInitialComponent());

    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, toolComp, glossaryMarkerCtr.getInitialComponent(), "course" + this.course.getResourceableId());       
    } else {
      columnLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), luTree, toolComp, contentP, "courseRun" + this.course.getResourceableId());             
    }
    listenTo(columnLayoutCtr);

    // activate the custom course css if any
    setCustomCSS(CourseFactory.getCustomCourseCss(ureq.getUserSession(), uce.getCourseEnvironment()));

    // decide what is show fist - a tool since activaded this way or the regular
    // course view
    if (showAssessmentTool) {
      launchAssessmentTool(ureq, assessmentViewIdentifier);
      coursemain = createVelocityContainer("index");
      coursemain.put("coursemain", all);
    } else {
      all.setContent(columnLayoutCtr.getInitialComponent());
      coursemain = createVelocityContainer("index");
      // 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", all);
    }
   
    putInitialPanel(coursemain);
   
    //disposed message controller
    //must be created beforehand
    Panel empty = new Panel("empty");//empty panel set as "menu" and "tool"
    Controller courseCloser = CourseFactory.createDisposedCourseRestartController(ureq, wControl, courseRepositoryEntry.getResourceableId());
    Controller disposedRestartController = new LayoutMain3ColsController(ureq, wControl, empty, empty, courseCloser.getInitialComponent(), "disposed course" + this.course.getResourceableId());
    setDisposedMsgController(disposedRestartController);
   
    // add as listener to course so we are being notified about course events:
    // - publish changes
    // - assessment events
    // - listen for CourseConfig events
    CoordinatorManager.getCoordinator().getEventBus().registerFor(this, identity, course);
    // - group modification events
    CoordinatorManager.getCoordinator().getEventBus().registerFor(this, identity, courseRepositoryEntry);   
   
    //eventAgency for identity per course counting purposes       
    CoordinatorManager.getCoordinator().getEventBus().registerFor(this, identity, courseRunOres);   
  }
 
  /**
   * @param ureq
   */
  private void createCourseGroupChatLink(UserRequest ureq) {
    //do not listen to this controller, gets disposed when logged out
    courseChatManagerCtr = InstantMessagingModule.getAdapter().getGroupChatManagerController(ureq);
    if (courseChatManagerCtr != null) {
      courseChatManagerCtr.createGroupChat(ureq, getWindowControl(), course, getExtendedCourseTitle(ureq.getLocale()), true, true);
    }
  }

  /**
   * @param locale
   * @return
   */
  private String getExtendedCourseTitle(Locale locale) {
    String extendedCourseTitle = this.courseTitle;
    RepositoryEntry repositoryEntry = RepositoryManager.getInstance().lookupRepositoryEntry(course, false);
    if (repositoryEntry != null && RepositoryManager.getInstance().createRepositoryEntryStatus(repositoryEntry.getStatusCode()).isClosed()) {
      PackageTranslator pT = new PackageTranslator(RepositoryEntryStatus.class.getPackage().getName(), locale);
      extendedCourseTitle = "[" + pT.translate("title.prefix.closed") + "] ".concat(extendedCourseTitle);
    }
    return extendedCourseTitle;
  }

  private void initUserRolesAndRights(final Identity identity) {
    CourseGroupManager cgm = this.course.getCourseEnvironment().getCourseGroupManager();
    // 1) course admins: users who are in repository entry owner group
    // if user has the role InstitutionalResourceManager and has the same institution like author
    // then set isCourseAdmin true
    isCourseAdmin = cgm.isIdentityCourseAdministrator(identity) | RepositoryManager.getInstance().isInstitutionalRessourceManagerFor(courseRepositoryEntry, identity);
    // 2) course coaches: users who are in the owner group of any group of this
    // course
    isCourseCoach = cgm.isIdentityCourseCoach(identity);
    // 3) all other rights are defined in the groupmanagement using the learning
    // group rights
    courseRightsCache.put(CourseRights.RIGHT_GROUPMANAGEMENT, new Boolean(cgm.hasRight(identity, CourseRights.RIGHT_GROUPMANAGEMENT)));
    courseRightsCache.put(CourseRights.RIGHT_COURSEEDITOR, new Boolean(cgm.hasRight(identity, CourseRights.RIGHT_COURSEEDITOR)));
    courseRightsCache.put(CourseRights.RIGHT_ARCHIVING, new Boolean(cgm.hasRight(identity, CourseRights.RIGHT_ARCHIVING)));
    courseRightsCache.put(CourseRights.RIGHT_ASSESSMENT, new Boolean(cgm.hasRight(identity, CourseRights.RIGHT_ASSESSMENT)));
    courseRightsCache.put(CourseRights.RIGHT_GLOSSARY, new Boolean(cgm.hasRight(identity, CourseRights.RIGHT_GLOSSARY)));
    courseRightsCache.put(CourseRights.RIGHT_STATISTICS, new Boolean(cgm.hasRight(identity, CourseRights.RIGHT_STATISTICS)));
  }

  /**
   * 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) {
    // build menu (treemodel)
    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) {
        nclr = navHandler.evaluateJumpToCourseNode(ureq, getWindowControl(), null, null, null);
      }
      if (!nclr.isVisible()) {
        MessageController msgController = MessageUIFactory.createInfoMessage(ureq, this.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);
    CourseNode courseNode = nclr.getCalledCourseNode();
    updateState(courseNode);

    // dispose old node controller
    if (currentNodeController != null) {
      currentNodeController.dispose();
    }
    currentNodeController = nclr.getRunController();
    contentP.setContent(currentNodeController.getInitialComponent());
    // enableCustomCourseCSS(ureq);

    return true;
  }

  private void updateState(CourseNode courseNode) {
    // TODO: fj : describe when the course node can be null
    if (courseNode == null) return;
    String selNodeId = courseNode.getIdent();
    setState("n"+selNodeId);
  }
 
  protected void adjustState(ControllerState cstate, UserRequest ureq) {
    String state = cstate.getSerializedState();
    char com = state.charAt(0);
    switch (com) {
      case 'n': // show node command
        String nodeId = state.substring(1);
        currentCourseNode = course.getRunStructure().getNode(nodeId);
        updateTreeAndContent(ureq, currentCourseNode, null);
        break;
      default: throw new AssertException("unknown command:"+state);
    }
  }

  /**
   * @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)
   */
  public void event(UserRequest ureq, Component source, Event event) {   
    if (source == luTree) {
      if (event.getCommand().equals(MenuTree.COMMAND_TREENODE_CLICKED)) {
        TreeEvent tev = (TreeEvent) event;
        if(this.assessmentChangedEventReceived) {
          uce.getScoreAccounting().evaluateAll();
          assessmentChangedEventReceived = false;
        }
       
        //getWindowControl().setInfo("time: "+System.currentTimeMillis());
        // 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);
        if (!nclr.isVisible()) {
          getWindowControl().setWarning(translate("msg.nodenotavailableanymore"));
          return;
        }
        // a click to a subtree's node
        if (nclr.isHandledBySubTreeModelListener()) return;

        // set the new treemodel
        treeModel = nclr.getTreeModel();
        luTree.setTreeModel(treeModel);

        // set the new tree selection
        String nodeId = nclr.getSelectedNodeId();
        luTree.setSelectedNodeId(nodeId);
        currentCourseNode = nclr.getCalledCourseNode();
        updateState(currentCourseNode);

        // dispose old node controller
        if (currentNodeController != null) {
          currentNodeController.dispose();
        }

        currentNodeController = nclr.getRunController();
        Component nodeComp = currentNodeController.getInitialComponent();
        contentP.setContent(nodeComp);
       
        // 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);
        }
       
        //re set current user count but not every click
        if (currentUserCount != null && event.hashCode() % 2 == 0) {
          OLATResourceable courseRunOres = OresHelper.createOLATResourceableInstance(RunMainController.ORES_TYPE_COURSE_RUN, course.getResourceableId());
          int cUsers = CoordinatorManager.getCoordinator().getEventBus().getListeningIdentityCntFor(courseRunOres);
          if (cUsers == 0) cUsers = 1;
          currentUserCount.setCustomDisplayText("&nbsp;&nbsp;&nbsp;&nbsp;"+String.valueOf(cUsers)+" "+getTranslator().translate("participants.in.course"));
          currentUserCount.setEnabled(false);
        }
      }
    } else if (source == coursemain) {
      if (event.getCommand().equals("activateCourseNode")) {
        // Events from the JS function o_activateCourseNode() - activate the given node id
        String nodeid = ureq.getModuleURI();
        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"));
        }
      }
    }
  }
   
  /**
   * @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) {
    // event from the current tool (editor, groupmanagement, archiver)   
    if (source == currentToolCtr) {
      if (event == Event.DONE_EVENT) {
        // for all other tools just pop
        all.setContent(columnLayoutCtr.getInitialComponent());
        // special check for editor
        if (isInEditor) {
          isInEditor = false; // for clarity
          currentToolCtr = null;
          if (needsRebuildAfterPublish) {
            needsRebuildAfterPublish = false;
           
            // rebuild up the running structure for this user, after publish;
            uce = new UserCourseEnvironmentImpl(ureq.getUserSession().getIdentityEnvironment(), CourseFactory.loadCourse(course.getResourceableId()).getCourseEnvironment());
            // build score now
            uce.getScoreAccounting().evaluateAll();
            navHandler = new NavigationHandler(uce, false);
           
            // rebuild and jump to root node
            updateTreeAndContent(ureq, null, null);
            // and also tools (maybe new assessable nodes -> show efficiency
            // statment)
            removeAsListenerAndDispose(toolC);
            toolC = initToolController(ureq.getIdentity(), ureq);
            Component toolComp = (toolC == null ? null : toolC.getInitialComponent());
            columnLayoutCtr.setCol2(toolComp);
          }
        } else {
          // release current node controllers resources and do cleanup if it was not the editor!
          removeAsListenerAndDispose(currentToolCtr);
          currentToolCtr = null;
        }
      }
    } else 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);
          }
          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 (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 (source == toolC) {
      String cmd = event.getCommand();
      doHandleToolEvents(ureq, cmd);

    } else if (source == bookmarkController) {
      if (event.equals(Event.DONE_EVENT)) {
        toolC.setEnabled(TOOL_BOOKMARK, false);
      } else if (event.equals(Event.CANCELLED_EVENT)) {
        toolC.setEnabled(TOOL_BOOKMARK, true);
      }
      updateTreeAndContent(ureq, currentCourseNode, null);
    } else if (source == glossaryToolCtr) {
      //fire info to IFrameDisplayController
      Long courseID = course.getResourceableId();
      // must work with SP and CP nodes, IFrameDisplayController listens to this event and expects "ICourse" resources.
      String oresName = ICourse.class.getSimpleName();
      ureq.getUserSession().getSingleUserEventCenter().fireEventToListenersOf(new MultiUserEvent(event.getCommand()), OresHelper.createOLATResourceableInstance(oresName, courseID));
    }
  }

  private void doHandleToolEvents(UserRequest ureq, String cmd) {
    if (cmd.indexOf(CMD_START_GROUP_PREFIX) == 0) {
      // launch the group in a new top nav tab
      String groupIdent = cmd.substring(CMD_START_GROUP_PREFIX.length());
      Long groupKey = new Long(Long.parseLong(groupIdent));
      BusinessGroupManager groupManager = BusinessGroupManagerImpl.getInstance();
      BusinessGroup group = groupManager.loadBusinessGroup(groupKey, false);
      // check if the group still exists and the user is really in this group
      // (security, changed group)
      if (group != null && groupManager.isIdentityInBusinessGroup(ureq.getIdentity(), group)) {
        Manager securityManager = ManagerFactory.getManager();
        boolean isCoach = securityManager.isIdentityInSecurityGroup(ureq.getIdentity(), group.getOwnerGroup());
        // create group without admin flag enabled eventhough the user might be
        // coach. the flag is not needed here
        // since the groups knows itself if the user is coach and the user sees
        // only his own groups.
        BGControllerFactory.getInstance().createRunControllerAsTopNavTab(group, ureq, getWindowControl(), /*ual, */false, null);
      } else {
        // display error and do logging
        getWindowControl().setError(translate("error.invalid.group"));
        logAudit("User tried to launch a group but user is not owner or participant "
            + "of group or group doesn't exist. Hacker attack or group has been changed or deleted. group key :: " + groupKey, null);
        // refresh toolbox that contained wrong group
        initGroupMemberships(ureq.getIdentity());
        removeAsListenerAndDispose(toolC);
        toolC = initToolController(ureq.getIdentity(), ureq);
        Component toolComp = (toolC == null ? null : toolC.getInitialComponent());
        columnLayoutCtr.setCol2(toolComp);

      }
    } else if (cmd.equals(COMMAND_EDIT)) {
      if (hasCourseRight(CourseRights.RIGHT_COURSEEDITOR) || isCourseAdmin) {
        Controller ec = CourseFactory.createEditorController(ureq, getWindowControl(), course);
        //user activity logger which was initialised with course run
        //
        if(ec != null){
          //we are in editing mode
          currentToolCtr = ec;
          listenTo(currentToolCtr);
          //currentToolCtr.addControllerListener(this); //deprecated, replaced by listenTo from BasicController
          isInEditor = true;
          all.setContent(currentToolCtr.getInitialComponent());
        }else{
          //editor could not be created
          //message already pushed as Error Box
        }
       
      } else throw new OLATSecurityException("wanted to activate editor, but no according right");

    } else if (cmd.equals("groupmngt")) {
      if (hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT) || isCourseAdmin) {
        currentToolCtr = new CourseGroupManagementMainController(ureq, getWindowControl(), course, BusinessGroup.TYPE_LEARNINGROUP);
        listenTo(currentToolCtr);
        all.setContent(currentToolCtr.getInitialComponent());
      } else throw new OLATSecurityException("clicked groupmanagement, but no according right");

    } else if (cmd.equals("rightmngt")) {
      if (isCourseAdmin) {
        currentToolCtr = new CourseGroupManagementMainController(ureq, getWindowControl(), course, BusinessGroup.TYPE_RIGHTGROUP);
        listenTo(currentToolCtr);
        all.setContent(currentToolCtr.getInitialComponent());
      } else throw new OLATSecurityException("clicked rightmanagement, but no according right");

    } else if (cmd.equals("statistic")) {
      if (hasCourseRight(CourseRights.RIGHT_STATISTICS) || isCourseAdmin) {
        currentToolCtr = new StatisticMainController(ureq, getWindowControl(), course);
        listenTo(currentToolCtr);
        all.setContent(currentToolCtr.getInitialComponent());
      } else throw new OLATSecurityException("clicked statistic, but no according right");

    }else if (cmd.equals("archiver")) {
      if (hasCourseRight(CourseRights.RIGHT_ARCHIVING) || isCourseAdmin) {
        currentToolCtr = new ArchiverMainController(ureq, getWindowControl(), course, new IArchiverCallback() {
          public boolean mayArchiveQtiResults() {
            return true;
          }

          public boolean mayArchiveLogfiles() {
            return true;
          }

          public boolean mayArchiveCoursestructure() {
            return true;
          }

          public boolean mayArchiveProperties() {
            return true;
          }

          public boolean mayArchiveHandedInTasks() {
            return true;
          }

          public boolean mayArchiveForums() {
            return true;
          }

          public boolean mayArchiveDialogs() {
            return true;
          }

          public boolean mayBringTogether() {
            return true;
          }

          public boolean mayArchiveWikis() {
            return true;
          }

          public boolean mayArchiveProjectBroker() {
            return true;
          }

        });
        listenTo(currentToolCtr);
        all.setContent(currentToolCtr.getInitialComponent());
      } else throw new OLATSecurityException("clicked archiver, but no according right");

    } else if (cmd.equals("assessment")) {
      launchAssessmentTool(ureq, null);

    } else if (cmd.equals("efficiencystatement")) {
      // will not be disposed on course run dispose, popus up as new
      // browserwindow
      ControllerCreator ctrlCreator = new ControllerCreator() {
        public Controller createController(UserRequest lureq, WindowControl lwControl) {
          EfficiencyStatementController efficiencyStatementController = new EfficiencyStatementController(lwControl, lureq, courseRepositoryEntry.getKey());
          efficiencyStatementController.setCustomCSS(CourseFactory.getCustomCourseCss(lureq.getUserSession(), uce.getCourseEnvironment()));
          return efficiencyStatementController;
        }         
      };
      //wrap the content controller into a full header layout
      ControllerCreator layoutCtrlr = BaseFullWebappPopupLayoutFactory.createAuthMinimalPopupLayout(ureq, ctrlCreator);
      //open in new browser window
      openInNewBrowserWindow(ureq, layoutCtrlr);
      //
    } else if (cmd.equals("personalnote")) {
      // will not be disposed on course run dispose, popus up as new
      // browserwindow
      ControllerCreator ctrlCreator = new ControllerCreator() {
        public Controller createController(UserRequest lureq, WindowControl lwControl) {
          Controller notesCtr = new NoteController(lureq, course, getExtendedCourseTitle(lureq.getLocale()), lwControl);
          LayoutMain3ColsController layoutCtr = new LayoutMain3ColsController(lureq, lwControl, null, null, notesCtr.getInitialComponent(), null);
          layoutCtr.setCustomCSS(CourseFactory.getCustomCourseCss(lureq.getUserSession(), uce.getCourseEnvironment()));
          layoutCtr.addDisposableChildController(notesCtr); // dispose glossary on layout dispose
          return layoutCtr;
        }         
      };
      //wrap the content controller into a full header layout
      ControllerCreator popupLayoutCtr = BaseFullWebappPopupLayoutFactory.createAuthMinimalPopupLayout(ureq, ctrlCreator);
      //open in new browser window
      openInNewBrowserWindow(ureq, popupLayoutCtr);
      //
    } else if (cmd.equals(TOOLBOX_LINK_COURSECONFIG)) {
      //FIXME:pb:a better workflows to link a course detail page from course run
      //was brasato getWindowControl().getDTabs().activateStatic(ureq, RepositorySite.class.getName(),RepositoryMainController.JUMPFROMEXTERN + RepositoryMainController.JUMPFROMCOURSE + courseRepositoryEntry.getKey().toString());
      DTabs dts = (DTabs)Windows.getWindows(ureq).getWindow(ureq).getAttribute("DTabs");
      if(dts != null){
        dts.activateStatic(ureq, RepositorySite.class.getName(),RepositoryMainController.JUMPFROMEXTERN + RepositoryMainController.JUMPFROMCOURSE + courseRepositoryEntry.getKey().toString());
      }else{
        //help course in popup window can not display detail page
        getWindowControl().setInfo("detail can not be displayed here");
      }
      return;
    } else if (cmd.equals(ACTION_BOOKMARK)) { // add bookmark
      RepositoryEntry re = RepositoryManager.getInstance().lookupRepositoryEntry(course, true);
      bookmarkController = new AddAndEditBookmarkController(ureq, getWindowControl(), re.getDisplayname(), "", re, re.getOlatResource()
          .getResourceableTypeName());
      listenTo(bookmarkController);
      contentP.pushContent(bookmarkController.getInitialComponent());
    } else if (cmd.equals(ACTION_CALENDAR)) { // popup calendar
      ControllerCreator ctrlCreator = new ControllerCreator() {
        public Controller createController(UserRequest lureq, WindowControl lwControl) {
          ContextEntry ce = BusinessControlFactory.getInstance().createContextEntry(courseRepositoryEntry);
          WindowControl llwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ce, lwControl);
          CourseCalendarController calendarController = new CourseCalendarController(lureq, llwControl, course);         
          // use a one-column main layout
          LayoutMain3ColsController layoutCtr = new LayoutMain3ColsController(lureq, llwControl, null, null, calendarController.getInitialComponent(), null);
          layoutCtr.setCustomCSS(CourseFactory.getCustomCourseCss(lureq.getUserSession(), uce.getCourseEnvironment()));
          layoutCtr.addDisposableChildController(calendarController); // dispose calendar on layout dispose
          return layoutCtr;         
        }         
      };
      //wrap the content controller into a full header layout
      ControllerCreator layoutCtrlr = BaseFullWebappPopupLayoutFactory.createAuthMinimalPopupLayout(ureq, ctrlCreator);
      //open in new browser window
      PopupBrowserWindow pbw = getWindowControl().getWindowBackOffice().getWindowManager().createNewPopupBrowserWindowFor(ureq, layoutCtrlr);
      pbw.open(ureq);
      //
    }
  }

  private void launchAssessmentTool(UserRequest ureq, String viewIdentifier) {
    // 1) course admins and users with tool right: full access
    if (hasCourseRight(CourseRights.RIGHT_ASSESSMENT) || isCourseAdmin) {
      AssessmentMainController assessmentToolCtr = new AssessmentMainController(ureq, getWindowControl(), course,
          new FullAccessAssessmentCallback());
      assessmentToolCtr.activate(ureq, viewIdentifier);
      currentToolCtr = assessmentToolCtr;
      listenTo(currentToolCtr);
      all.setContent(currentToolCtr.getInitialComponent());
    }
    // 2) users with coach right: limited access to coached groups
    else if (isCourseCoach) {
      AssessmentMainController assessmentToolCtr = new AssessmentMainController(ureq, getWindowControl(), course,
          new CoachingGroupAccessAssessmentCallback());
      assessmentToolCtr.activate(ureq, viewIdentifier);
      currentToolCtr = assessmentToolCtr;
      listenTo(currentToolCtr);
      all.setContent(currentToolCtr.getInitialComponent());
    } else throw new OLATSecurityException("clicked assessment tool in course::" + course.getResourceableId()
        + ", but no right to launch it. Username::" + ureq.getIdentity().getName());
  }

  /**
   * implementation of listener which listens to publish events
   *
   * @see org.olat.core.util.event.GenericEventListener#event(org.olat.core.gui.control.Event)
   */
  public void event(Event event) {   
    if (event instanceof PublishEvent) {
      PublishEvent pe = (PublishEvent) event;
      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...
        return;
      }
      if (!course.getResourceableId().equals(pe.getPublishedCourseResId())) throw new AssertException("not the same course");
      // long pts = pe.getLatestPublishTimestamp();
      // disable this controller and issue a information
      // FIXME:fj:b - implement the 'there are 5 users using this course at the
      // moment' warning (in the publishcontroller)
      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 {
        dispose();
      }
    } 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)) {
        dispose();
      }
    } 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;                   
        } else if (assessmentChangeType.equals(AssessmentChangedEvent.TYPE_EFFICIENCY_STATEMENT_CHANGED)) {
          // update tools, maybe efficiency statement link has changed
          removeAsListenerAndDispose(toolC);
          toolC = initToolController(identity, null);
          Component toolComp = (toolC == null ? null : toolC.getInitialComponent());
          columnLayoutCtr.setCol2(toolComp);
        }
        // raise a flag to indicate refresh
        needsRebuildAfterRunDone = true;
      }
    } else if (event instanceof BusinessGroupModifiedEvent) {
      BusinessGroupModifiedEvent bgme = (BusinessGroupModifiedEvent) event;
      Identity identity = uce.getIdentityEnvironment().getIdentity();
      // only do something if this identity is affected by change and the action
      // was adding or removing of the user
      if (bgme.wasMyselfAdded(identity) || bgme.wasMyselfRemoved(identity)) {
        // 1) reinitialize all group memberships
        initGroupMemberships(identity);
        // 2) reinitialize the users roles and rights
        initUserRolesAndRights(identity);
        // 3) rebuild toolboxes with link to groups and tools
        removeAsListenerAndDispose(toolC);
        toolC = initToolController(identity, null);
        Component toolComp = (toolC == null ? null : toolC.getInitialComponent());
        columnLayoutCtr.setCol2(toolComp);
        needsRebuildAfterRunDone = true;
      } else if (bgme.getCommand().equals(BusinessGroupModifiedEvent.GROUPRIGHTS_MODIFIED_EVENT)) {
        // check if this affects a right group where the user does participate.
        // if so, we need
        // to rebuild the toolboxes
        if (PersistenceHelper.listContainsObjectByKey(rightGroups, bgme.getModifiedGroupKey())) {
          // 1) reinitialize all group memberships
          initGroupMemberships(identity);
          // 2) reinitialize the users roles and rights
          initUserRolesAndRights(identity);
          // 3) rebuild toolboxes with link to groups and tools
          removeAsListenerAndDispose(toolC);
          toolC = initToolController(identity, null);
          Component toolComp = (toolC == null ? null : toolC.getInitialComponent());
          columnLayoutCtr.setCol2(toolComp);
        }
      }

    } else if (event instanceof CourseConfigEvent) {       
      dispose();
     
    } else if (event instanceof EntryChangedEvent && ((EntryChangedEvent)event).getChange()!=EntryChangedEvent.MODIFIED_AT_PUBLISH) {
      //courseRepositoryEntry changed (e.g. fired at course access rule change)
      EntryChangedEvent repoEvent = (EntryChangedEvent) event;     
      if (courseRepositoryEntry.getKey().equals(repoEvent.getChangedEntryKey()) && repoEvent.getChange() == EntryChangedEvent.MODIFIED) {       
        dispose();       
      }
    }
  }
   

  /**
   * Initializes the course tools according to the users rights (repository
   * author group and course rights in course groupmanagement)
   *
   * @param identity
   * @param ureq
   * @param ureq
   * @return ToolController
   */
  private ToolController initToolController(Identity identity, UserRequest ureq) {
    // guest never have a tool controller
    if (isGuest) return null;

    ToolController myTool = ToolFactory.createToolController(getWindowControl());
    CourseConfig cc = uce.getCourseEnvironment().getCourseConfig();

    // 1) administrative tools
    if (isCourseAdmin || isCourseCoach || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR)
        || hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT) || hasCourseRight(CourseRights.RIGHT_ARCHIVING)
        || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) {
      myTool.addHeader(translate("header.tools"));
      if (hasCourseRight(CourseRights.RIGHT_COURSEEDITOR) || isCourseAdmin) {
        myTool.addLink(COMMAND_EDIT, translate("command.openeditor"));
      }
      if (hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT) || isCourseAdmin) {
        myTool.addLink("groupmngt", translate("command.opengroupmngt"));
      }
      if (isCourseAdmin) {
        myTool.addLink("rightmngt", translate("command.openrightmngt"));
      }
      if (hasCourseRight(CourseRights.RIGHT_ARCHIVING) || isCourseAdmin) {
        myTool.addLink("archiver", translate("command.openarchiver"));
      }
      if (hasCourseRight(CourseRights.RIGHT_ASSESSMENT) || isCourseCoach || isCourseAdmin) {
        myTool.addLink("assessment", translate("command.openassessment"));
      }
      /*
       * http://bugs.olat.org/jira/browse/OLAT-4928
       */
      if (hasCourseRight(CourseRights.RIGHT_STATISTICS) || isCourseAdmin) {
        myTool.addLink("statistic", translate("command.openstatistic"));
      }
     
      //
      /*
       * if (isCourseAdmin) { myTool.addLink(TOOLBOX_LINK_COURSECONFIG,
       * translate("command.courseconfig")); }
       */
      //
    }

    // 2) add coached groups
    if (ownedGroups.size() > 0) {
      myTool.addHeader(translate("header.tools.ownerGroups"));
      Iterator iter = ownedGroups.iterator();
      while (iter.hasNext()) {
        BusinessGroup group = (BusinessGroup) iter.next();
        myTool.addLink(CMD_START_GROUP_PREFIX + group.getKey().toString(), group.getName());
      }
    }

    // 3) add participating groups
    if (participatedGroups.size() > 0) {
      myTool.addHeader(translate("header.tools.participatedGroups"));
      Iterator iter = participatedGroups.iterator();
      while (iter.hasNext()) {
        BusinessGroup group = (BusinessGroup) iter.next();
        myTool.addLink(CMD_START_GROUP_PREFIX + group.getKey().toString(), group.getName());
      }
    }

    // 4) add right groups
    if (rightGroups.size() > 0) {
      myTool.addHeader(translate("header.tools.rightGroups"));
      Iterator iter = rightGroups.iterator();
      while (iter.hasNext()) {
        BusinessGroup group = (BusinessGroup) iter.next();
        myTool.addLink(CMD_START_GROUP_PREFIX + group.getKey().toString(), group.getName());
      }
    }

    // 5) add waiting-list groups
    if (waitingListGroups.size() > 0) {
      myTool.addHeader(translate("header.tools.waitingListGroups"));
      Iterator iter = waitingListGroups.iterator();
      while (iter.hasNext()) {
        BusinessGroup group = (BusinessGroup) iter.next();
        BusinessGroupManager businessGroupManager = BusinessGroupManagerImpl.getInstance();
        int pos = businessGroupManager.getPositionInWaitingListFor(identity, group);
        myTool.addLink(CMD_START_GROUP_PREFIX + group.getKey().toString(), group.getName() + "(" + pos + ")", group
            .getKey().toString(), null);
        myTool.setEnabled(group.getKey().toString(), false);
      }
    }

    // new toolbox 'general'
    myTool.addHeader(translate("header.tools.general"));
    if (cc.isCalendarEnabled()) {
      myTool.addPopUpLink(ACTION_CALENDAR, translate("command.calendar"), null, null, "950", "750", false);
    }
    if (cc.hasGlossary()) {
      myTool.addComponent(glossaryToolCtr.getInitialComponent());
    }
    if (showCourseConfigLink) {
      myTool.addLink(TOOLBOX_LINK_COURSECONFIG, translate("command.courseconfig"));
    }
    myTool.addPopUpLink("personalnote", translate("command.personalnote"), null, null, "750", "550", false);
    if (offerBookmark && !isGuest) {
      myTool.addLink(ACTION_BOOKMARK, translate("command.bookmark"), TOOL_BOOKMARK, null);
      BookmarkManager bm = BookmarkManager.getInstance();
      if (bm.isResourceableBookmarked(identity, courseRepositoryEntry)) myTool.setEnabled(TOOL_BOOKMARK, false);

    }
    if (cc.isEfficencyStatementEnabled() && course.hasAssessableNodes()) {
      // link to efficiency statements should
      // - not appear when not configured in course configuration
      // - not appear when configured in course configuration but no assessable
      // node exist
      // - appear but dimmed when configured, assessable node exist but no
      // assessment data exists for user
      // - appear as link when configured, assessable node exist and assessment
      // data exists for user
      myTool.addPopUpLink("efficiencystatement", translate("command.efficiencystatement"), "command.efficiencystatement", null,
          "750", "800", false);
      EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance();
      EfficiencyStatement es = esm.getUserEfficiencyStatement(courseRepositoryEntry.getKey(), identity);
      if (es == null) {
        myTool.setEnabled("command.efficiencystatement", false);
      }
    }
   
    //add group chat to toolbox
    boolean instantMsgYes = InstantMessagingModule.isEnabled();
    boolean chatIsEnabled = CourseModule.isCourseChatEnabled() &&  cc.isChatEnabled();
    if (instantMsgYes && chatIsEnabled) {
      // we add the course chat link controller to the toolbox
      if (courseChatManagerCtr == null && ureq != null) createCourseGroupChatLink(ureq);
      if (courseChatManagerCtr != null){
        Controller groupChatController = courseChatManagerCtr.getGroupChatController(course);
        if(groupChatController !=null){
          myTool.addComponent(groupChatController.getInitialComponent());
          addCurrentUserCount(myTool);
        }
      }
    } else {
      addCurrentUserCount(myTool);
    }

    listenTo(myTool);
    return myTool;
  }

  private void addCurrentUserCount(ToolController myTool) {
    //add currentUser count to toolbox but only if not yet the chatRoom above provides this info
    if ((courseChatManagerCtr == null) || (courseChatManagerCtr != null && courseChatManagerCtr.isLazyCreateChat())) {
      OLATResourceable courseRunOres = OresHelper.createOLATResourceableInstance(RunMainController.ORES_TYPE_COURSE_RUN, course.getResourceableId());
      int cUsers = CoordinatorManager.getCoordinator().getEventBus().getListeningIdentityCntFor(courseRunOres);
      if (cUsers == 0) cUsers = 1;
      VelocityContainer currentUsers = createVelocityContainer("currentUsers");
      currentUsers.contextPut("currentUsers", cUsers);
      currentUserCount = LinkFactory.createCustomLink("currentUsers", "cUsers", "", Link.NONTRANSLATED, currentUsers, this);
      currentUserCount.setCustomDisplayText("&nbsp;&nbsp;&nbsp;&nbsp;"+String.valueOf(cUsers)+" "+getTranslator().translate("participants.in.course"));
      currentUserCount.setCustomEnabledLinkCSS("b_toolbox_link");
      currentUserCount.setTooltip(getTranslator().translate("participants.in.course.desc"), false);
      currentUserCount.setEnabled(false);
      myTool.addComponent(currentUserCount);
    }
  }

  /**
   * Reads the users learning group rights from a local hash map. use
   * initCourseRightCache() to initialize the cache.
   *
   * @param right The name of a learning group right
   * @return true if the user has this right, false otherwise.
   */
  private boolean hasCourseRight(String right) {
    Boolean bool = (Boolean) courseRightsCache.get(right);
    return bool.booleanValue();
  }

  /**
   * Initialize the users group memberships for groups used within this course
   *
   * @param identity
   */
  private void initGroupMemberships(Identity identity) {
    CourseGroupManager cgm = course.getCourseEnvironment().getCourseGroupManager();
    ownedGroups = cgm.getOwnedLearningGroupsFromAllContexts(identity);
    participatedGroups = cgm.getParticipatingLearningGroupsFromAllContexts(identity);
    waitingListGroups = cgm.getWaitingListGroupsFromAllContexts(identity);
    rightGroups = cgm.getParticipatingRightGroupsFromAllContexts(identity);
  }

  /**
   * @see org.olat.core.gui.control.DefaultController#doDispose(boolean)
   */
  protected void doDispose() {
    // remove as listener from this course events:
    // - group modification events
    CoordinatorManager.getCoordinator().getEventBus().deregisterFor(this, courseRepositoryEntry);
    // - publish changes
    // - assessment events
    // TODO:pb: also here - CourseConfig events are removed
    CoordinatorManager.getCoordinator().getEventBus().deregisterFor(this, course);
    //remove as listener from course run eventAgency
    CoordinatorManager.getCoordinator().getEventBus().deregisterFor(this, courseRunOres);

    // those controllers are disposed by BasicController:
    toolC = null;   
    currentToolCtr = null;
    columnLayoutCtr = null;
    bookmarkController = null;
    glossaryMarkerCtr = null;
    glossaryToolCtr = null;
       
    // currentNodeController must be disposed manually does not work with
    // general BasicController methods
    if (currentNodeController != null) {
      currentNodeController.dispose();
      currentNodeController = null;
    }

    // instant messaging: send presence like "leaving course: Biology I" as
    // awareness info.
    if (InstantMessagingModule.isEnabled()&& CourseModule.isCourseChatEnabled() && !isGuest) {
      if (courseChatManagerCtr != null) courseChatManagerCtr.removeChat(course);
      String intro = translate("course.presence.message.leave") + " " + getExtendedCourseTitle(getLocale());
      InstantMessagingModule.getAdapter().sendStatus(uce.getIdentityEnvironment().getIdentity().getName(), intro);
    }
    // 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());
  }

  /**
   * @see org.olat.core.gui.control.generic.dtabs.Activateable#activate(org.olat.core.gui.UserRequest,
   *      java.lang.String)
   */
  public void activate(UserRequest ureq, String viewIdentifier) {
    if(isDisposed()) {
      return;
    }
    // transform ACTIVATE_RUN to activate course root node
    if (viewIdentifier.equals(RepositoryDetailsController.ACTIVATE_RUN)) {
      // activating the run, means so far activating the root node
      // viewIdentifier = course.getRunStructure().getRootNode().getIdent();
      return;
    }
    //

    if (viewIdentifier.equals(RepositoryDetailsController.ACTIVATE_EDITOR)) {
      // activate editor
      if (!isInEditor) {
        if (currentToolCtr == null) {
          // not already in the editor and also no other course tool running
          isInEditor = true;
          doHandleToolEvents(ureq, COMMAND_EDIT);
        } else if (currentToolCtr != null) {
          // not activateable as another tool is running, i.e. assessment tool
          getWindowControl().setWarning(translate("warn.cannotactivatesinceintool"));
        }
      }// else it is already active
    } else if (isInEditor || currentToolCtr != null) {
      // do not activate anything if a course tool or the editor is active!
      getWindowControl().setWarning(translate("warn.cannotactivatesinceintool"));
    } else {
      if (currentNodeController != null) {
        currentNodeController.dispose();
      }
     
      CourseNode cn = null;
      cn = (viewIdentifier == null ? null : course.getRunStructure().getNode(viewIdentifier));
      String subsubId = null;

      // check for subsubIdent (jump into the third level)
      if (viewIdentifier != null) {
        if (viewIdentifier.indexOf(":") != -1) {
          String[] vis = viewIdentifier.split(":");
          cn = course.getRunStructure().getNode(vis[0]);
          subsubId = vis[1];
        }
      }
      // FIXME:fj:b is this needed in some cases?: currentCourseNode = cn;
      getWindowControl().makeFlat();
     
      //add loggin information for case course gets started via jumpin link/search
      addLoggingResourceable(LoggingResourceable.wrap(course));
      if (cn!=null) {
        addLoggingResourceable(LoggingResourceable.wrap(cn));
      }
      updateTreeAndContent(ureq, cn, subsubId);
    }
   
    // reset gloss toolC to get newly tabs
    removeAsListenerAndDispose(glossaryToolCtr);
    boolean hasGlossaryRights = hasCourseRight(CourseRights.RIGHT_GLOSSARY) || isCourseAdmin;
    glossaryToolCtr = new CourseGlossaryToolLinkController(getWindowControl(), ureq, course, getTranslator(), hasGlossaryRights,
        uce.getCourseEnvironment(), glossaryMarkerCtr);
    listenTo(glossaryToolCtr);
  }
}
TOP

Related Classes of org.olat.course.run.RunMainController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.