/**
  * INTERNAL METHOD<br>
  * Get the email address from system settings or the evaluation
  *
  * @param eval
  * @return an email address
  * @throws IllegalStateException if a from address cannot be found
  */
 public String getFromEmailOrFail(EvalEvaluation eval) {
   String from = (String) settings.get(EvalSettings.FROM_EMAIL_ADDRESS);
   if (eval.getReminderFromEmail() != null && !"".equals(eval.getReminderFromEmail())) {
     from = eval.getReminderFromEmail();
   }
   if (from == null) {
     throw new IllegalStateException(
         "Could not get a from email address from system settings or the evaluation");
   }
   return from;
 }
 /* (non-Javadoc)
  * @see uk.org.ponder.rsf.flow.ActionResultInterceptor#interceptActionResult(uk.org.ponder.rsf.flow.ARIResult, uk.org.ponder.rsf.viewstate.ViewParameters, java.lang.Object)
  */
 public void interceptActionResult(
     ARIResult result, ViewParameters incoming, Object actionReturn) {
   // for evaluation creation
   if (incoming instanceof EvalViewParameters
       && result.resultingView instanceof EvalViewParameters) {
     EvalViewParameters in = (EvalViewParameters) incoming;
     EvalViewParameters outgoing = (EvalViewParameters) result.resultingView;
     if (in.evaluationId != null) {
       outgoing.evaluationId = in.evaluationId;
     } else {
       EvalEvaluation eval =
           (EvalEvaluation) evaluationBeanLocator.locateBean(EvaluationBeanLocator.NEW_1);
       if (eval != null) {
         outgoing.evaluationId = eval.getId();
       }
     }
   }
 }
  /* (non-Javadoc)
   * @see uk.org.ponder.rsf.view.ComponentProducer#fillComponents(uk.org.ponder.rsf.components.UIContainer, uk.org.ponder.rsf.viewstate.ViewParameters, uk.org.ponder.rsf.view.ComponentChecker)
   */
  public void fillComponents(
      UIContainer tofill, ViewParameters viewparams, ComponentChecker checker) {

    EvalViewParameters evalViewParams = (EvalViewParameters) viewparams;
    if (evalViewParams.evaluationId == null) {
      throw new IllegalArgumentException("Cannot access this view unless the evaluationId is set");
    }

    String actionBean = "setupEvalBean.";
    String evaluationOTP = "evaluationBeanLocator." + evalViewParams.evaluationId + ".";
    /**
     * This is the evaluation we are working with on this page, this should ONLY be read from, do
     * not change any of these fields
     */
    EvalEvaluation evaluation = evaluationService.getEvaluationById(evalViewParams.evaluationId);
    String currentEvalState = evaluationService.returnAndFixEvalState(evaluation, true);

    // local variables used in the render logic
    String currentUserId = commonLogic.getCurrentUserId();
    boolean userAdmin = commonLogic.isUserAdmin(currentUserId);
    boolean createTemplate = authoringService.canCreateTemplate(currentUserId);
    boolean beginEvaluation = evaluationService.canBeginEvaluation(currentUserId);

    DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.LONG, locale);
    DateFormat timeFormat = DateFormat.getTimeInstance(DateFormat.SHORT, locale);

    /*
     * top links here
     */
    UIInternalLink.make(
        tofill,
        "summary-link",
        UIMessage.make("summary.page.title"),
        new SimpleViewParameters(SummaryProducer.VIEW_ID));

    if (userAdmin) {
      UIInternalLink.make(
          tofill,
          "administrate-link",
          UIMessage.make("administrate.page.title"),
          new SimpleViewParameters(AdministrateProducer.VIEW_ID));
      UIInternalLink.make(
          tofill,
          "control-scales-link",
          UIMessage.make("controlscales.page.title"),
          new SimpleViewParameters(ControlScalesProducer.VIEW_ID));
    }

    if (createTemplate) {
      UIInternalLink.make(
          tofill,
          "control-templates-link",
          UIMessage.make("controltemplates.page.title"),
          new SimpleViewParameters(ControlTemplatesProducer.VIEW_ID));
      UIInternalLink.make(
          tofill,
          "control-items-link",
          UIMessage.make("controlitems.page.title"),
          new SimpleViewParameters(ControlItemsProducer.VIEW_ID));
    }

    if (beginEvaluation) {
      UIInternalLink.make(
          tofill,
          "control-evaluations-link",
          UIMessage.make("controlevaluations.page.title"),
          new SimpleViewParameters(ControlEvaluationsProducer.VIEW_ID));
    } else {
      throw new SecurityException(
          "User attempted to access " + VIEW_ID + " when they are not allowed");
    }

    UIInternalLink.make(
        tofill,
        "eval-settings-link",
        UIMessage.make("evalsettings.page.title"),
        new EvalViewParameters(EvaluationSettingsProducer.VIEW_ID, evalViewParams.evaluationId));
    if (EvalConstants.EVALUATION_STATE_PARTIAL.equals(evaluation.getState())) {
      // creating a new eval
      UIMessage.make(tofill, "eval-start-text", "starteval.page.title");
    }

    UIForm form = UIForm.make(tofill, "evalSettingsForm");

    // REOPENING eval (SPECIAL CASE)
    Date reOpenDueDate = null;
    Date reOpenStopDate = null;
    boolean reOpening = false;
    if (evalViewParams.reOpening) {
      Boolean enableReOpen = (Boolean) settings.get(EvalSettings.ENABLE_EVAL_REOPEN);
      if (enableReOpen) {
        // check if already active, do nothing if not closed
        if (EvalUtils.checkStateAfter(
            currentEvalState, EvalConstants.EVALUATION_STATE_CLOSED, false)) {
          // force the due and stop dates to something reasonable
          Calendar calendar = new GregorianCalendar();
          calendar.setTime(new Date());
          calendar.add(Calendar.DATE, 1);
          reOpenDueDate = calendar.getTime();

          Boolean useStopDate = (Boolean) settings.get(EvalSettings.EVAL_USE_STOP_DATE);
          if (useStopDate) {
            // assign stop date to equal due date for now
            reOpenStopDate = calendar.getTime();
          }

          // finally force the state to appear active
          currentEvalState = EvalConstants.EVALUATION_STATE_ACTIVE;
          form.parameters.add(
              new UIELBinding(actionBean + "reOpening", true)); // so we know we are reopening
          reOpening = true;
        }
      }
    }

    // EVALUATION TITLE/INSTRUCTIONS

    if (EvalUtils.checkStateBefore(currentEvalState, EvalConstants.EVALUATION_STATE_ACTIVE, true)) {
      UIInput.make(form, "title", evaluationOTP + "title");
    } else {
      UIOutput.make(tofill, "title_disabled", evaluation.getTitle());
    }

    if (EvalUtils.checkStateBefore(currentEvalState, EvalConstants.EVALUATION_STATE_CLOSED, true)) {
      UIInput instructionsInput =
          UIInput.make(form, "instructions:", evaluationOTP + "instructions");
      instructionsInput.decorators = new DecoratorList(new UITextDimensionsDecorator(60, 4));
      richTextEvolver.evolveTextInput(instructionsInput);
    } else {
      UIVerbatim.make(tofill, "instructions_disabled", evaluation.getInstructions());
    }

    // only put up the controls/auto binding if this is not already set
    if (evaluation.getTemplate() == null) {
      // Make bottom table containing the list of templates if no template set
      if (evalViewParams.templateId == null) {
        // get the templates usable by this user
        List<EvalTemplate> templateList =
            authoringService.getTemplatesForUser(currentUserId, null, false);
        if (templateList.size() > 0) {
          UIBranchContainer chooseTemplate = UIBranchContainer.make(form, "chooseTemplate:");

          String[] values = new String[templateList.size()];
          String[] labels = new String[templateList.size()];

          // TODO - this is not a good way to do this
          UISelect radios =
              UISelect.make(
                  chooseTemplate, "templateRadio", null, null, actionBean + "templateId", null);
          String selectID = radios.getFullID();
          for (int i = 0; i < templateList.size(); i++) {
            EvalTemplate template = templateList.get(i);
            values[i] = template.getId().toString();
            labels[i] = template.getTitle();
            UIBranchContainer radiobranch =
                UIBranchContainer.make(chooseTemplate, "templateOptions:", i + "");
            UISelectChoice.make(radiobranch, "radioValue", selectID, i);
            UISelectLabel.make(radiobranch, "radioLabel", selectID, i);
            EvalUser owner = commonLogic.getEvalUserById(template.getOwner());
            UIOutput.make(radiobranch, "radioOwner", owner.displayName);
            UIInternalLink.make(
                radiobranch,
                "viewPreview_link",
                UIMessage.make("starteval.view.preview.link"),
                new EvalViewParameters(PreviewEvalProducer.VIEW_ID, null, template.getId()));
          }
          // need to assign the choices and labels at the end here since we used nulls at the
          // beginning
          radios.optionlist = UIOutputMany.make(values);
          radios.optionnames = UIOutputMany.make(labels);
        } else {
          throw new IllegalStateException(
              "User got to evaluation settings when they have no access to any templates... "
                  + "producer suicide was the only way out");
        }
      } else {
        // just bind in the template explicitly to the evaluation
        form.parameters.add(
            new UIELBinding(
                evaluationOTP + "template",
                new ELReference("templateBeanLocator." + evalViewParams.templateId)));
      }
    } else {
      EvalTemplate template = authoringService.getTemplateById(evaluation.getTemplate().getId());
      UIBranchContainer showTemplateBranch = UIBranchContainer.make(tofill, "showTemplate:");
      UIMessage.make(
          showTemplateBranch,
          "eval_template_title",
          "evalsettings.template.title.display",
          new Object[] {template.getTitle()});
      UIInternalLink.make(
          showTemplateBranch,
          "eval_template_preview_link",
          UIMessage.make("evalsettings.template.preview.link"),
          new EvalViewParameters(PreviewEvalProducer.VIEW_ID, null, template.getId()));
      if (!template.getLocked().booleanValue()
          && authoringService.canModifyTemplate(currentUserId, template.getId())) {
        UIInternalLink.make(
            showTemplateBranch,
            "eval_template_modify_link",
            UIMessage.make("general.command.edit"),
            new TemplateViewParameters(ModifyTemplateItemsProducer.VIEW_ID, template.getId()));
      }
    }

    // EVALUATION DATES

    Date today = new Date();
    UIMessage.make(
        tofill,
        "current_date",
        "evalsettings.dates.current",
        new Object[] {dateFormat.format(today), timeFormat.format(today)});

    // retrieve the global setting for use of date only or date and time picker
    Boolean useDateTime = (Boolean) settings.get(EvalSettings.EVAL_USE_DATE_TIME);

    // Start Date
    UIBranchContainer showStartDate = UIBranchContainer.make(form, "showStartDate:");
    generateDateSelector(
        showStartDate,
        "startDate",
        evaluationOTP + "startDate",
        null,
        currentEvalState,
        EvalConstants.EVALUATION_STATE_ACTIVE,
        useDateTime);

    // Due Date
    UIBranchContainer showDueDate = UIBranchContainer.make(form, "showDueDate:");
    generateDateSelector(
        showDueDate,
        "dueDate",
        evaluationOTP + "dueDate",
        reOpenDueDate,
        currentEvalState,
        EvalConstants.EVALUATION_STATE_GRACEPERIOD,
        useDateTime);

    // Stop Date - Show the "Stop date" text box only if allowed in the System settings
    Boolean useStopDate = (Boolean) settings.get(EvalSettings.EVAL_USE_STOP_DATE);
    if (useStopDate) {
      UIBranchContainer showStopDate = UIBranchContainer.make(form, "showStopDate:");
      generateDateSelector(
          showStopDate,
          "stopDate",
          evaluationOTP + "stopDate",
          reOpenStopDate,
          currentEvalState,
          EvalConstants.EVALUATION_STATE_CLOSED,
          useDateTime);
    }

    // EVALUATION RESULTS VIEWING/SHARING

    // radio buttons for the results sharing options
    UISelect resultsSharingRadios =
        UISelect.make(
                form,
                "dummyRadioSharing",
                EvalToolConstants.EVAL_RESULTS_SHARING_VALUES,
                EvalToolConstants.EVAL_RESULTS_SHARING_LABELS_PROPS,
                evaluationOTP + "resultsSharing",
                null)
            .setMessageKeys();
    String resultsSharingId = resultsSharingRadios.getFullID();
    for (int i = 0; i < EvalToolConstants.EVAL_RESULTS_SHARING_VALUES.length; ++i) {
      UIBranchContainer radiobranch = UIBranchContainer.make(form, "resultsSharingChoice:", i + "");
      UISelectChoice choice = UISelectChoice.make(radiobranch, "radioValue", resultsSharingId, i);
      UISelectLabel.make(radiobranch, "radioLabel", resultsSharingId, i)
          .decorate(new UILabelTargetDecorator(choice));
    }

    // show the view date only if allowed by system settings
    if (((Boolean) settings.get(EvalSettings.EVAL_USE_VIEW_DATE)).booleanValue()) {
      UIBranchContainer showViewDate = UIBranchContainer.make(form, "showViewDate:");
      generateDateSelector(
          showViewDate,
          "viewDate",
          evaluationOTP + "viewDate",
          null,
          currentEvalState,
          EvalConstants.EVALUATION_STATE_VIEWABLE,
          useDateTime);
    }

    // all types of users view results on the same date or we can configure the results viewing
    // separately
    boolean sameViewDateForAll = (Boolean) settings.get(EvalSettings.EVAL_USE_SAME_VIEW_DATES);

    // Student view date
    Boolean studentViewResults = (Boolean) settings.get(EvalSettings.STUDENT_ALLOWED_VIEW_RESULTS);
    UIBranchContainer showResultsToStudents =
        UIBranchContainer.make(form, "showResultsToStudents:");
    generateSettingsControlledCheckbox(
        showResultsToStudents,
        "studentViewResults",
        evaluationOTP + "studentViewResults",
        studentViewResults,
        form,
        EvalUtils.checkStateAfter(currentEvalState, EvalConstants.EVALUATION_STATE_VIEWABLE, true));
    generateViewDateControl(
        showResultsToStudents,
        "studentsViewDate",
        evaluationOTP + "studentsDate",
        studentViewResults,
        useDateTime,
        sameViewDateForAll);

    // Instructor view date
    Boolean instructorViewResults =
        (Boolean) settings.get(EvalSettings.INSTRUCTOR_ALLOWED_VIEW_RESULTS);
    UIBranchContainer showResultsToInst = UIBranchContainer.make(form, "showResultsToInst:");
    generateSettingsControlledCheckbox(
        showResultsToInst,
        "instructorViewResults",
        evaluationOTP + "instructorViewResults",
        instructorViewResults,
        form,
        EvalUtils.checkStateAfter(currentEvalState, EvalConstants.EVALUATION_STATE_VIEWABLE, true));
    generateViewDateControl(
        showResultsToInst,
        "instructorsViewDate",
        evaluationOTP + "instructorsDate",
        instructorViewResults,
        useDateTime,
        sameViewDateForAll);

    // RESPONDENT SETTINGS

    // Student Allowed Leave Unanswered
    Boolean studentUnanswersAllowed =
        (Boolean) settings.get(EvalSettings.STUDENT_ALLOWED_LEAVE_UNANSWERED);
    UIBranchContainer showBlankQuestionAllowedToStut =
        UIBranchContainer.make(form, "showBlankQuestionAllowedToStut:");
    generateSettingsControlledCheckbox(
        showBlankQuestionAllowedToStut,
        "blankResponsesAllowed",
        evaluationOTP + "blankResponsesAllowed",
        studentUnanswersAllowed,
        form,
        EvalUtils.checkStateAfter(currentEvalState, EvalConstants.EVALUATION_STATE_ACTIVE, true));

    // Student Modify Responses
    Boolean studentModifyReponses = (Boolean) settings.get(EvalSettings.STUDENT_MODIFY_RESPONSES);
    UIBranchContainer showModifyResponsesAllowedToStu =
        UIBranchContainer.make(form, "showModifyResponsesAllowedToStu:");
    generateSettingsControlledCheckbox(
        showModifyResponsesAllowedToStu,
        "modifyResponsesAllowed",
        evaluationOTP + "modifyResponsesAllowed",
        studentModifyReponses,
        form,
        EvalUtils.checkStateAfter(currentEvalState, EvalConstants.EVALUATION_STATE_ACTIVE, true));

    // ADMIN SETTINGS SECTION

    UISelect authControlSelect =
        UISelect.make(
                form,
                "auth-control-choose",
                EvalToolConstants.AUTHCONTROL_VALUES,
                EvalToolConstants.AUTHCONTROL_LABELS,
                evaluationOTP + "authControl")
            .setMessageKeys();
    if (EvalUtils.checkStateAfter(currentEvalState, EvalConstants.EVALUATION_STATE_ACTIVE, true)) {
      RSFUtils.disableComponent(authControlSelect);
    }

    if (userAdmin) {
      // If the person is an admin (any kind), then we need to show these instructor opt in/out
      // settings

      UIMessage.make(
          form, "instructor-opt-instructions", "evalsettings.admin.settings.instructions");
      UIMessage.make(form, "instructor-opt-header", "evalsettings.instructor.opt.desc");

      // If "EvalSettings.INSTRUCTOR_MUST_USE_EVALS_FROM_ABOVE" is set as configurable
      // i.e. NULL in the database then show the instructor opt select box. Else just show the value
      // as label
      String instUseFromAboveValue =
          (String) settings.get(EvalSettings.INSTRUCTOR_MUST_USE_EVALS_FROM_ABOVE);
      if (instUseFromAboveValue == null
          || EvalToolConstants.ADMIN_BOOLEAN_CONFIGURABLE.equals(instUseFromAboveValue)) {
        UISelect instOpt =
            UISelect.make(
                    form,
                    "instructorOpt",
                    EvalToolConstants.INSTRUCTOR_OPT_VALUES,
                    EvalToolConstants.INSTRUCTOR_OPT_LABELS,
                    evaluationOTP + "instructorOpt")
                .setMessageKeys();
        if (EvalUtils.checkStateAfter(
            currentEvalState, EvalConstants.EVALUATION_STATE_INQUEUE, true)) {
          RSFUtils.disableComponent(instOpt);
        }
      } else {
        int index =
            ArrayUtil.indexOf(EvalToolConstants.INSTRUCTOR_OPT_VALUES, instUseFromAboveValue);
        String instUseFromAboveLabel = EvalToolConstants.INSTRUCTOR_OPT_LABELS[index];
        // Displaying the label corresponding to INSTRUCTOR_MUST_USE_EVALS_FROM_ABOVE value set as
        // system property
        UIMessage.make(form, "instructorOptLabel", instUseFromAboveLabel);
        // Doing the binding of this INSTRUCTOR_MUST_USE_EVALS_FROM_ABOVE value so that it can be
        // saved in the database
        form.parameters.add(
            new UIELBinding(evaluationOTP + "instructorOpt", instUseFromAboveValue));
      }
    }

    // EVALUATION REMINDERS SECTION

    // email available template link
    UIInternalLink.make(
        form,
        "emailAvailable_link",
        UIMessage.make("evalsettings.available.mail.link"),
        new EmailViewParameters(
            PreviewEmailProducer.VIEW_ID,
            null,
            EvalConstants.EMAIL_TEMPLATE_AVAILABLE,
            evaluation.getId()));

    // email reminder control
    UISelect reminderDaysSelect =
        UISelect.make(
                form,
                "reminderDays",
                EvalToolConstants.REMINDER_EMAIL_DAYS_VALUES,
                EvalToolConstants.REMINDER_EMAIL_DAYS_LABELS,
                evaluationOTP + "reminderDays")
            .setMessageKeys();
    if (EvalUtils.checkStateAfter(
        currentEvalState, EvalConstants.EVALUATION_STATE_GRACEPERIOD, true)) {
      RSFUtils.disableComponent(reminderDaysSelect);
    }

    // email reminder template link
    UIInternalLink.make(
        form,
        "emailReminder_link",
        UIMessage.make("evalsettings.reminder.mail.link"),
        new EmailViewParameters(
            PreviewEmailProducer.VIEW_ID,
            null,
            EvalConstants.EMAIL_TEMPLATE_REMINDER,
            evaluation.getId()));

    // email from address control
    String defaultEmail = (String) settings.get(EvalSettings.FROM_EMAIL_ADDRESS);
    UIMessage.make(
        form, "eval-from-email-note", "evalsettings.email.sent.from", new String[] {defaultEmail});
    UIInput.make(form, "reminderFromEmail", evaluationOTP + "reminderFromEmail");

    // EVALUATION EXTRAS SECTION
    Boolean categoriesEnabled = (Boolean) settings.get(EvalSettings.ENABLE_EVAL_CATEGORIES);
    if (categoriesEnabled) {
      UIBranchContainer extrasBranch = UIBranchContainer.make(form, "showEvalExtras:");

      // eval category
      if (categoriesEnabled) {
        UIBranchContainer categoryBranch = UIBranchContainer.make(extrasBranch, "showCategory:");
        UIInput.make(categoryBranch, "eval-category", evaluationOTP + "evalCategory");
        if (evaluation.getEvalCategory() != null) {
          UILink.make(
                  categoryBranch,
                  "eval-category-direct-link",
                  UIMessage.make("general.direct.link"),
                  commonLogic.getEntityURL(
                      EvalCategoryEntityProvider.ENTITY_PREFIX, evaluation.getEvalCategory()))
              .decorate(new UITooltipDecorator(UIMessage.make("general.direct.link.title")));
        }
      }
    }

    // EVAL SETTINGS SAVING CONTROLS
    // if this evaluation is already saved, show "Save Settings" button else this is the "Continue
    // to Assign to Courses" button
    String messageKey = "evalsettings.save.settings.link";
    if (EvalConstants.EVALUATION_STATE_PARTIAL.equals(evaluation.getState())) {
      messageKey = "evalsettings.continue.assigning.link";
    }
    if (reOpening) {
      messageKey = "evalsettings.reopening.eval.link";
    }
    UICommand.make(
        form,
        "continueAssigning",
        UIMessage.make(messageKey),
        actionBean + "completeSettingsAction");
    UIMessage.make(tofill, "cancel-button", "general.cancel.button");

    // this fills in the javascript call (areaId, selectId, selectValue, reminderId)
    // NOTE: RSF bug causes us to have to generate the ids manually
    // (http://www.caret.cam.ac.uk/jira/browse/RSF-65)
    UIInitBlock.make(
        tofill,
        "initJavascript",
        "EvalSystem.initEvalSettings",
        new Object[] {
          "evaluation_reminder_area",
          authControlSelect.getFullID() + "-selection",
          EvalConstants.EVALUATION_AUTHCONTROL_NONE,
          reminderDaysSelect.getFullID() + "-selection"
        });
  }
  /* (non-Javadoc)
   * @see org.sakaiproject.evaluation.logic.EvalEmailsLogic#sendEvalCreatedNotifications(java.lang.Long, boolean)
   */
  public String[] sendEvalCreatedNotifications(Long evaluationId, boolean includeOwner) {
    log.debug("evaluationId: " + evaluationId + ", includeOwner: " + includeOwner);

    EvalEvaluation eval = getEvaluationOrFail(evaluationId);
    String from = getFromEmailOrFail(eval);
    EvalEmailTemplate emailTemplate =
        getEmailTemplateOrFail(EvalConstants.EMAIL_TEMPLATE_CREATED, evaluationId);

    Map<String, String> replacementValues = new HashMap<String, String>();
    replacementValues.put("HelpdeskEmail", from);

    // setup the opt-in, opt-out, and add questions variables
    int addItems = ((Integer) settings.get(EvalSettings.ADMIN_ADD_ITEMS_NUMBER)).intValue();
    if (!eval.getInstructorOpt().equals(EvalConstants.INSTRUCTOR_REQUIRED) || (addItems > 0)) {
      if (eval.getInstructorOpt().equals(EvalConstants.INSTRUCTOR_OPT_IN)) {
        // if eval is opt-in notify instructors that they may opt in
        replacementValues.put("ShowOptInText", "true");
      } else if (eval.getInstructorOpt().equals(EvalConstants.INSTRUCTOR_OPT_OUT)) {
        // if eval is opt-out notify instructors that they may opt out
        replacementValues.put("ShowOptOutText", "true");
      }
      if (addItems > 0) {
        // if eval allows instructors to add questions notify instructors they may add questions
        replacementValues.put("ShowAddItemsText", "true");
      }
    }

    String message = emailTemplate.getMessage();

    // get the associated groups for this evaluation
    Map<Long, List<EvalGroup>> evalGroups =
        evaluationService.getEvalGroupsForEval(new Long[] {evaluationId}, true, null);

    // only one possible map key so we can assume evaluationId
    List<EvalGroup> groups = evalGroups.get(evaluationId);
    if (log.isDebugEnabled()) {
      log.debug("Found " + groups.size() + " groups for new evaluation: " + evaluationId);
    }

    List<String> sentEmails = new ArrayList<String>();
    // loop through contexts and send emails to correct users in each evalGroupId
    for (int i = 0; i < groups.size(); i++) {
      EvalGroup group = (EvalGroup) groups.get(i);
      if (EvalConstants.GROUP_TYPE_INVALID.equals(group.type)) {
        continue; // skip processing for invalid groups
      }

      Set<String> userIdsSet =
          commonLogic.getUserIdsForEvalGroup(group.evalGroupId, EvalConstants.PERM_BE_EVALUATED);
      // add in the owner or remove them based on the setting
      if (includeOwner) {
        userIdsSet.add(eval.getOwner());
      } else {
        if (userIdsSet.contains(eval.getOwner())) {
          userIdsSet.remove(eval.getOwner());
        }
      }

      // skip ahead if there is no one to send to
      if (userIdsSet.size() == 0) continue;

      // turn the set into an array
      String[] toUserIds = (String[]) userIdsSet.toArray(new String[] {});
      if (log.isDebugEnabled()) {
        log.debug(
            "Found "
                + toUserIds.length
                + " users ("
                + toUserIds
                + ") to send "
                + EvalConstants.EMAIL_TEMPLATE_CREATED
                + " notification to for new evaluation ("
                + evaluationId
                + ") and evalGroupId ("
                + group.evalGroupId
                + ")");
      }

      // replace the text of the template with real values
      message = makeEmailMessage(message, eval, group, replacementValues);
      String subject = makeEmailMessage(emailTemplate.getSubject(), eval, group, replacementValues);

      // send the actual emails for this evalGroupId
      String[] emailAddresses =
          commonLogic.sendEmailsToUsers(from, toUserIds, subject, message, true);
      log.info(
          "Sent evaluation created message to "
              + emailAddresses.length
              + " users (attempted to send to "
              + toUserIds.length
              + ")");
      // store sent emails to return
      for (int j = 0; j < emailAddresses.length; j++) {
        sentEmails.add(emailAddresses[j]);
      }
      commonLogic.registerEntityEvent(EVENT_EMAIL_CREATED, eval);
    }

    return (String[]) sentEmails.toArray(new String[] {});
  }
  /**
   * INTERNAL METHOD<br>
   * Builds the email message from a template and a bunch of variables (passed in and otherwise)
   *
   * @param messageTemplate
   * @param eval
   * @param group
   * @param replacementValues a map of String -> String representing $keys in the template to
   *     replace with text values
   * @return the processed message template with replacements and logic handled
   */
  public String makeEmailMessage(
      String messageTemplate,
      EvalEvaluation eval,
      EvalGroup group,
      Map<String, String> replacementValues) {
    // replace the text of the template with real values
    if (replacementValues == null) {
      replacementValues = new HashMap<String, String>();
    }
    replacementValues.put("EvalTitle", eval.getTitle());

    // use a date which is related to the current users locale
    DateFormat df =
        DateFormat.getDateInstance(
            DateFormat.MEDIUM, commonLogic.getUserLocale(commonLogic.getCurrentUserId()));

    replacementValues.put("EvalStartDate", df.format(eval.getStartDate()));
    String dueDate = "--------";
    if (eval.getDueDate() != null) {
      dueDate = df.format(eval.getDueDate());
    }
    replacementValues.put("EvalDueDate", dueDate);
    String viewDate = null;
    if (eval.getViewDate() != null) {
      viewDate = df.format(eval.getDueDate());
    } else {
      viewDate = dueDate;
    }
    replacementValues.put("EvalResultsDate", viewDate);
    replacementValues.put("EvalGroupTitle", group.title);

    // ensure that the if-then variables are set to false if they are unset
    if (!replacementValues.containsKey("ShowAddItemsText")) {
      replacementValues.put("ShowAddItemsText", "false");
    }
    if (!replacementValues.containsKey("ShowOptInText")) {
      replacementValues.put("ShowOptInText", "false");
    }
    if (!replacementValues.containsKey("ShowOptOutText")) {
      replacementValues.put("ShowOptOutText", "false");
    }

    // generate URLs to the evaluation
    String evalEntityURL = null;
    if (group != null && group.evalGroupId != null) {
      // get the URL directly to the evaluation with group context included
      Long assignGroupId = evaluationService.getAssignGroupId(eval.getId(), group.evalGroupId);
      EvalAssignGroup assignGroup = evaluationService.getAssignGroupById(assignGroupId);
      if (assignGroup != null) {
        evalEntityURL = commonLogic.getEntityURL(assignGroup);
      }
    }

    if (evalEntityURL == null) {
      // just get the URL to the evaluation without group context
      evalEntityURL = commonLogic.getEntityURL(eval);
    }

    // all URLs are identical because the user permissions determine access uniquely
    replacementValues.put("URLtoTakeEval", evalEntityURL);
    replacementValues.put("URLtoAddItems", evalEntityURL);
    replacementValues.put("URLtoOptIn", evalEntityURL);
    replacementValues.put("URLtoOptOut", evalEntityURL);
    replacementValues.put("URLtoViewResults", evalEntityURL);
    replacementValues.put("URLtoSystem", commonLogic.getServerUrl());

    return TextTemplateLogicUtils.processTextTemplate(messageTemplate, replacementValues);
  }
  /* (non-Javadoc)
   * @see org.sakaiproject.evaluation.logic.EvalEmailsLogic#sendEvalResultsNotifications(java.lang.Long, boolean, boolean, java.lang.String)
   */
  public String[] sendEvalResultsNotifications(
      Long evaluationId, boolean includeEvaluatees, boolean includeAdmins, String jobType) {
    log.debug(
        "evaluationId: "
            + evaluationId
            + ", includeEvaluatees: "
            + includeEvaluatees
            + ", includeAdmins: "
            + includeAdmins);

    /*TODO deprecated?
    if(EvalConstants.EVAL_INCLUDE_ALL.equals(includeConstant)) {
    }
    boolean includeEvaluatees = true;
    if (includeEvaluatees) {
    // TODO Not done yet
    log.error("includeEvaluatees Not implemented");
    }
    */

    EvalEvaluation eval = getEvaluationOrFail(evaluationId);
    String from = getFromEmailOrFail(eval);
    EvalEmailTemplate emailTemplate =
        getEmailTemplateOrFail(EvalConstants.EMAIL_TEMPLATE_RESULTS, evaluationId);

    // get the associated eval groups for this evaluation
    Map<Long, List<EvalGroup>> evalGroupIds =
        evaluationService.getEvalGroupsForEval(new Long[] {evaluationId}, false, null);
    // only one possible map key so we can assume evaluationId
    List<EvalGroup> groups = evalGroupIds.get(evaluationId);
    if (log.isDebugEnabled())
      log.debug("Found " + groups.size() + " groups for available evaluation: " + evaluationId);
    Map<String, EvalGroup> groupsMap = new HashMap<String, EvalGroup>();
    for (EvalGroup evalGroup : groups) {
      groupsMap.put(evalGroup.evalGroupId, evalGroup);
    }

    // get the associated eval assign groups for this evaluation
    Map<Long, List<EvalAssignGroup>> evalAssignGroups =
        evaluationService.getAssignGroupsForEvals(new Long[] {evaluationId}, false, null);
    // only one possible map key so we can assume evaluationId
    List<EvalAssignGroup> assignGroups = evalAssignGroups.get(evaluationId);
    if (log.isDebugEnabled())
      log.debug(
          "Found "
              + assignGroups.size()
              + " assign groups for available evaluation: "
              + evaluationId);

    List<String> sentEmails = new ArrayList<String>();
    // loop through groups and send emails to correct users in each evalGroupId
    for (int i = 0; i < assignGroups.size(); i++) {
      EvalAssignGroup evalAssignGroup = assignGroups.get(i);
      String evalGroupId = evalAssignGroup.getEvalGroupId();
      EvalGroup group = groupsMap.get(evalGroupId);
      if (group == null || EvalConstants.GROUP_TYPE_INVALID.equals(group.type)) {
        log.warn(
            "Invalid group returned for groupId ("
                + evalGroupId
                + "), could not send results notifications");
        continue;
      }

      /*
       * Notification of results may occur on separate dates for owner,
       * instructors, and students. Job type is used to distinguish the
       * intended recipient group.
       */

      // always send results email to eval.getOwner()
      Set<String> userIdsSet = new HashSet<String>();
      if (jobType.equals(EvalConstants.JOB_TYPE_VIEWABLE)) {
        userIdsSet.add(eval.getOwner());
      }

      // if results are not private
      if (!EvalConstants.SHARING_PRIVATE.equals(eval.getResultsSharing())) {
        // at present, includeAdmins is always true
        if (includeAdmins
            && evalAssignGroup.getInstructorsViewResults().booleanValue()
            && jobType.equals(EvalConstants.JOB_TYPE_VIEWABLE_INSTRUCTORS)) {
          userIdsSet.addAll(
              commonLogic.getUserIdsForEvalGroup(evalGroupId, EvalConstants.PERM_BE_EVALUATED));
        }

        // at present, includeEvaluatees is always true
        if (includeEvaluatees
            && evalAssignGroup.getStudentsViewResults().booleanValue()
            && jobType.equals(EvalConstants.JOB_TYPE_VIEWABLE_STUDENTS)) {
          userIdsSet.addAll(
              commonLogic.getUserIdsForEvalGroup(evalGroupId, EvalConstants.PERM_TAKE_EVALUATION));
        }
      }

      if (userIdsSet.size() > 0) {
        // turn the set into an array
        String[] toUserIds = (String[]) userIdsSet.toArray(new String[] {});
        log.debug(
            "Found "
                + toUserIds.length
                + " users ("
                + toUserIds
                + ") to send "
                + EvalConstants.EMAIL_TEMPLATE_RESULTS
                + " notification to for available evaluation ("
                + evaluationId
                + ") and group ("
                + evalGroupId
                + ")");

        // replace the text of the template with real values
        Map<String, String> replacementValues = new HashMap<String, String>();
        replacementValues.put("HelpdeskEmail", from);
        String message =
            makeEmailMessage(emailTemplate.getMessage(), eval, group, replacementValues);
        String subject =
            makeEmailMessage(emailTemplate.getSubject(), eval, group, replacementValues);

        // send the actual emails for this evalGroupId
        String[] emailAddresses =
            commonLogic.sendEmailsToUsers(from, toUserIds, subject, message, true);
        log.info(
            "Sent evaluation results message to "
                + emailAddresses.length
                + " users (attempted to send to "
                + toUserIds.length
                + ")");
        // store sent emails to return
        for (int j = 0; j < emailAddresses.length; j++) {
          sentEmails.add(emailAddresses[j]);
        }
        commonLogic.registerEntityEvent(EVENT_EMAIL_RESULTS, eval);
      }
    }
    return (String[]) sentEmails.toArray(new String[] {});
  }
  /* (non-Javadoc)
   * @see org.sakaiproject.evaluation.logic.EvalEmailsLogic#sendEvalAvailableNotifications(java.lang.Long, boolean)
   */
  public String[] sendEvalAvailableNotifications(Long evaluationId, boolean includeEvaluatees) {
    log.debug("evaluationId: " + evaluationId + ", includeEvaluatees: " + includeEvaluatees);

    Set<String> userIdsSet = null;
    String message = null;
    boolean studentNotification = true;

    EvalEvaluation eval = getEvaluationOrFail(evaluationId);
    String from = getFromEmailOrFail(eval);
    EvalEmailTemplate emailTemplate =
        getEmailTemplateOrFail(EvalConstants.EMAIL_TEMPLATE_AVAILABLE, evaluationId);
    // get the instructor opt-in email template
    EvalEmailTemplate emailOptInTemplate =
        getEmailTemplateOrFail(EvalConstants.EMAIL_TEMPLATE_AVAILABLE_OPT_IN, null);

    // get the associated assign groups for this evaluation
    Map<Long, List<EvalAssignGroup>> evalAssignGroups =
        evaluationService.getAssignGroupsForEvals(new Long[] {evaluationId}, true, null);
    List<EvalAssignGroup> assignGroups = evalAssignGroups.get(evaluationId);

    List<String> sentEmails = new ArrayList<String>();
    // loop through groups and send emails to correct users group
    for (int i = 0; i < assignGroups.size(); i++) {
      EvalAssignGroup assignGroup = assignGroups.get(i);
      EvalGroup group = commonLogic.makeEvalGroupObject(assignGroup.getEvalGroupId());
      if (eval.getInstructorOpt().equals(EvalConstants.INSTRUCTOR_REQUIRED)) {
        // notify students
        userIdsSet =
            commonLogic.getUserIdsForEvalGroup(
                group.evalGroupId, EvalConstants.PERM_TAKE_EVALUATION);
        studentNotification = true;
      } else {
        // instructor may opt-in or opt-out
        if (assignGroup.getInstructorApproval().booleanValue()) {
          // instructor has opted-in, notify students
          userIdsSet =
              commonLogic.getUserIdsForEvalGroup(
                  group.evalGroupId, EvalConstants.PERM_TAKE_EVALUATION);
          studentNotification = true;
        } else {
          if (eval.getInstructorOpt().equals(EvalConstants.INSTRUCTOR_OPT_IN)
              && includeEvaluatees) {
            // instructor has not opted-in, notify instructors
            userIdsSet =
                commonLogic.getUserIdsForEvalGroup(
                    group.evalGroupId, EvalConstants.PERM_BE_EVALUATED);
            studentNotification = false;
          } else {
            userIdsSet = new HashSet<String>();
          }
        }
      }

      // skip ahead if there is no one to send to
      if (userIdsSet.size() == 0) continue;

      // turn the set into an array
      String[] toUserIds = (String[]) userIdsSet.toArray(new String[] {});

      if (log.isDebugEnabled()) {
        log.debug(
            "Found "
                + toUserIds.length
                + " users ("
                + toUserIds
                + ") to send "
                + EvalConstants.EMAIL_TEMPLATE_CREATED
                + " notification to for available evaluation ("
                + evaluationId
                + ") and group ("
                + group.evalGroupId
                + ")");
      }

      // replace the text of the template with real values
      Map<String, String> replacementValues = new HashMap<String, String>();
      replacementValues.put("HelpdeskEmail", from);

      // choose from 2 templates
      EvalEmailTemplate currentTemplate = emailTemplate;
      if (!studentNotification) {
        currentTemplate = emailOptInTemplate;
      }
      message = makeEmailMessage(currentTemplate.getMessage(), eval, group, replacementValues);
      String subject =
          makeEmailMessage(currentTemplate.getSubject(), eval, group, replacementValues);

      // send the actual emails for this evalGroupId
      String[] emailAddresses =
          commonLogic.sendEmailsToUsers(from, toUserIds, subject, message, true);
      log.info(
          "Sent evaluation available message to "
              + emailAddresses.length
              + " users (attempted to send to "
              + toUserIds.length
              + ")");
      // store sent emails to return
      for (int j = 0; j < emailAddresses.length; j++) {
        sentEmails.add(emailAddresses[j]);
      }
      commonLogic.registerEntityEvent(EVENT_EMAIL_AVAILABLE, eval);
    }

    return (String[]) sentEmails.toArray(new String[] {});
  }