/**
   * Returns the <code>SubscriptionContext</code> to use for assessment notification about specified
   * <code>ICourse</code>.<br>
   * <br>
   * <b>PRE CONDITIONS</b>
   *
   * <ul>
   *   <li><code>course != null</code>
   * </ul>
   *
   * If <code>ident == null</code>, the subscription context is (created and) returned without
   * authorization control
   *
   * @param ident the identity, if null, no subscription check will be made
   * @param course
   * @return the subscription context to use or <code>null</code> if the identity associated to the
   *     request is not allowed to be notified
   * @see #canSubscribeForAssessmentNotification(Identity, ICourse)
   */
  protected SubscriptionContext getAssessmentSubscriptionContext(Identity ident, ICourse course) {
    SubscriptionContext sctx = null;

    if (ident == null || canSubscribeForAssessmentNotification(ident, course)) {
      // Creates a new SubscriptionContext only if not found into cache
      Long courseId = course.getResourceableId();
      synchronized (
          subsContexts) { // o_clusterOK by:ld - no problem to have independent subsContexts caches
                          // for each cluster node
        sctx = subsContexts.get(courseId);
        if (sctx == null) {
          // a subscription context showing to the root node (the course's root
          // node is started when clicking such a notification)
          CourseNode cn = course.getRunStructure().getRootNode();
          CourseEnvironment ce = course.getCourseEnvironment();
          // FIXME:fg:b little problem is that the assessment tool and the course are not "the same"
          // anymore, that is you can open the same course twice in the
          // dynamic tabs by a) klicking e.g. via repo, and b via notifications link to the
          // assementtool
          sctx =
              new SubscriptionContext(
                  CourseModule.ORES_COURSE_ASSESSMENT, ce.getCourseResourceableId(), cn.getIdent());
          subsContexts.put(courseId, sctx);
        }
      }
    }

    return sctx;
  }
 private AssessableCourseNode getCourseNode() {
   ICourse course = CourseFactory.loadCourse(courseRes);
   CourseNode node = course.getRunStructure().getNode(courseNodeIdent);
   if (node instanceof AssessableCourseNode) {
     return (AssessableCourseNode) node;
   }
   return null;
 }
  /**
   * Utility method.<br>
   * Build (recursively) the list of all test nodes belonging to the specified <code>ICourse</code>.
   * <br>
   * The returned <code>List</code> is empty if course has no AssessableCourseNode. Structure course
   * node are excluded from the list.<br>
   * <br>
   * <b>PRE CONDITIONS</b>
   *
   * <ul>
   *   <li><code>course != null</code>
   * </ul>
   *
   * <br>
   * <b>POST CONDITIONS</b>
   *
   * <ul>
   *   <li>The returned list, if not empty, contains ONLY instances of type <code>
   *       AssessableCourseNode</code>
   * </ul>
   */
  private List<AssessableCourseNode> getCourseTestNodes(ICourse course) {
    List<AssessableCourseNode> assessableNodes = new ArrayList<AssessableCourseNode>();

    Structure courseStruct = course.getRunStructure();
    CourseNode rootNode = courseStruct.getRootNode();

    getCourseTestNodes(rootNode, assessableNodes);

    return assessableNodes;
  }
  private boolean isCourseCalendarEnabled(ICourse course) {
    if (course.getCourseConfig().isCalendarEnabled()) {
      return true;
    }

    CourseNode rootNode = course.getRunStructure().getRootNode();
    CalCourseNodeVisitor v = new CalCourseNodeVisitor();
    new TreeVisitor(v, rootNode, true).visitAll();
    return v.isFound();
  }
  private void sendFeedback(List<BulkAssessmentFeedback> feedbacks) {
    if (task == null) {
      log.error("Haven't a task to know creator and modifiers of the task", null);
      return;
    }

    Identity creator = task.getCreator();
    String language = creator.getUser().getPreferences().getLanguage();
    Locale locale = I18nManager.getInstance().getLocaleOrDefault(language);
    Translator translator =
        Util.createPackageTranslator(
            BulkAssessmentOverviewController.class,
            locale,
            Util.createPackageTranslator(AssessmentManager.class, locale));
    MailManager mailManager = CoreSpringFactory.getImpl(MailManager.class);
    TaskExecutorManager taskManager = CoreSpringFactory.getImpl(TaskExecutorManager.class);

    String feedbackStr = renderFeedback(feedbacks, translator);

    MailBundle mail = new MailBundle();
    mail.setToId(creator);
    mail.setFrom(WebappHelper.getMailConfig("mailReplyTo"));
    List<Identity> modifiers = taskManager.getModifiers(task);
    if (modifiers.size() > 0) {
      ContactList cc = new ContactList("CC");
      cc.addAllIdentites(modifiers);
      mail.setContactList(cc);
    }

    String businessPath = "";
    ICourse course = CourseFactory.loadCourse(courseRes);
    CourseNode node = course.getRunStructure().getNode(courseNodeIdent);
    String courseTitle = course.getCourseTitle();
    String nodeTitle = node.getShortTitle();
    String numOfAssessedIds = Integer.toString(datas == null ? 0 : datas.getRowsSize());
    String date = Formatter.getInstance(locale).formatDateAndTime(new Date());

    mail.setContext(new MailContextImpl(courseRes, courseNodeIdent, businessPath));
    String subject =
        translator.translate("confirmation.mail.subject", new String[] {courseTitle, nodeTitle});
    String body =
        translator.translate(
            "confirmation.mail.body",
            new String[] {courseTitle, nodeTitle, feedbackStr, numOfAssessedIds, date});
    mail.setContent(subject, body);
    mailManager.sendMessage(mail);
  }