/**
  * Returns a map containing the token name (<code>String</code>) as key and token value (<code>
  * String</code>) as value. This map can be used to substitute the token name with the token value
  * in a string.
  *
  * <p>Override this method to define additional substitution values for the token processor.
  *
  * @param request request context for the exit, assumed not <code>null</code>, used to obtain the
  *     values for the tokens
  * @return the map containing the token name and values. This map contains non-<code>null</code>
  *     value for all supported token names. The value may be empty if this token does not the
  *     corresponding property defined in "rxconfig/Workflow/rxworkflow.properties" file and a
  *     non-empty value could not be obtained from the request context object. If a non-<code>null
  *     </code> and non-empty value is obtained from the request context then it is used as the
  *     token value, otherwise the value configured in "rxconfig/Workflow/rxworkflow.properties"
  *     file is used (which defaults to empty if no property containing the token name as key is
  *     defined).
  */
 @Deprecated
 protected Map<String, String> getTokenValues(IPSRequestContext request) {
   Map<String, String> tokenValues = new HashMap<String, String>(MAIL_TOKENS_DEFAULT_VALUE);
   for (int i = 0; i < MAIL_TOKENS.length; i++) {
     String tokenName = MAIL_TOKENS[i];
     if (tokenName.equals(WORKFLOW_COMMENT_TOKEN)) {
       String transitionComment =
           PSWorkFlowUtils.getTransitionCommentFromHTMLParams(request.getParameters());
       if ((transitionComment != null) && (transitionComment.trim().length() > 0)) {
         tokenValues.put(tokenName, transitionComment);
       }
     }
   }
   return tokenValues;
 }
 /**
  * Sets the CC list. Override this method to change the CC list.
  *
  * @param request the users request context
  * @param messageCtx the current message
  * @param users the delimited string of users
  * @param communityId the community of the item being transitioned.
  */
 @SuppressWarnings("unchecked")
 protected void setCCAddress(
     IPSRequestContext request,
     IPSMailMessageWritable messageCtx,
     String users,
     String communityId) {
   if (users == null) {
     return;
   }
   users = users.trim();
   if (users.length() == 0) {
     return;
   }
   List<String> ccList =
       PSWorkFlowUtils.tokenizeString(users, PSWorkFlowUtils.EMAIL_STRING_DELIMITER);
   this.setCCAddress(request, messageCtx, ccList.iterator(), communityId);
 }
  /**
   * Executive method for sending mail notifications, Gets notifications, constructs recipient list
   * and sends the mail notifications.
   *
   * @param contentURL URL of the content item
   * @param workflowID WorkflowID for the content item
   * @param fromStateID ID of content state before transition
   * @param toStateID ID of content state after transition
   * @param userName name of user sending the notification param wfRoleInfo Object containing role
   *     info such as from and to state adhoc user information.
   * @param request request context for the exit
   * @param connection connection to back-end database
   * @param communityId the community id by which to filter the subjects to which the notifications
   *     are sent, may be <code>null</code> to ignore the ccommunity filter.
   * @throws PSMailException if an error occurs while sending the mail.
   * @throws SQLException if a database error occurs
   * @throws PSEntryNotFoundException if there is no data base entry for the content item.
   */
  @SuppressWarnings("unchecked")
  private void sendNotifications(
      String contentURL,
      int contentId,
      int workflowID,
      int transitionID,
      int fromStateID,
      int toStateID,
      String userName,
      PSWorkflowRoleInfo wfRoleInfo,
      IPSRequestContext request,
      Connection connection,
      String communityId)
      throws PSEntryNotFoundException, PSMailException, SQLException {
    PSWorkFlowUtils.printWorkflowMessage(request, "  Entering Method sendNotifications");

    List toStateUserList = new ArrayList();
    List fromStateUserList = new ArrayList();

    String emailCCString = "";
    PSNotificationsContext nc = null;
    PSTransitionNotificationsContext tnc = null;
    int notificationID = 0;
    String additionalRecipientString = "";
    String subject = "";
    String body = "";

    // Get the notification messages for the transition

    try {
      tnc = new PSTransitionNotificationsContext(workflowID, transitionID, connection);
      if (0 == tnc.getCount()) {
        log.info(
            "  There are no notifications for the transition "
                + transitionID
                + " in the workflow "
                + workflowID
                + ".");
        return;
      }

      if (!this.checkNotification(request, wfRoleInfo, tnc)) {
        log.debug("Notification not performed");
        return;
      }

      // Get whichever state role lists will be needed
      if (tnc.requireFromStateRoles()) {
        fromStateUserList =
            this.getStateUserList(
                request,
                contentId,
                wfRoleInfo,
                wfRoleInfo.getFromStateCauc(),
                workflowID,
                connection,
                fromStateID,
                communityId);
      }

      if (tnc.requireToStateRoles()) {
        toStateUserList =
            this.getStateUserList(
                request,
                contentId,
                wfRoleInfo,
                wfRoleInfo.getToStateCauc(),
                workflowID,
                connection,
                toStateID,
                communityId);
      }

      do // Loop over the notifications for this transition
      {
        IPSMailMessageWritable messageContext = buildMessageContext();

        this.setFromAddress(request, messageContext, userName, communityId);

        additionalRecipientString = "";

        notificationID = tnc.getNotificationID();

        // This will throw an exception if the notification does not exist
        nc = new PSNotificationsContext(workflowID, notificationID, connection);

        subject = nc.getSubject();
        body = nc.getBody();

        processTokens(request, contentId, messageContext, subject, body);

        additionalRecipientString = tnc.getAdditionalRecipientList();

        emailCCString = tnc.getCCList();

        this.setCCAddress(request, messageContext, emailCCString, communityId);

        if (tnc.notifyToStateRoles() && !toStateUserList.isEmpty()) {
          this.setToAddress(request, messageContext, toStateUserList.iterator(), communityId);
        }

        if (tnc.notifyFromStateRoles() && !fromStateUserList.isEmpty()) {
          this.setToAddress(request, messageContext, fromStateUserList.iterator(), communityId);
        }

        this.setToAddress(request, messageContext, additionalRecipientString, communityId);

        messageContext.setUrl(contentURL);

        if (messageContext.getToList().isEmpty()) {
          /*
           * A message must have "To" recipients. Therefore if there are
           * "CC" recipients but no "To"  recipients, send the mail "To"
           * the CC list. This is desired because they should get mail even
           * if all the roles have turned off notification.
           */
          Collection ccList = messageContext.getCCList();
          if (!ccList.isEmpty()) {
            messageContext.setTo(ccList);
            messageContext.setCC(Collections.EMPTY_LIST);
            log.info(
                "  There are no \"to\" recipients for notification  "
                    + notificationID
                    + " in the workflow "
                    + workflowID
                    + ". \"to\" email will be sent to the \"CC\" recipients");
          } else {
            log.info(
                "  There are no \"to\" or \"CC\"  recipients for notification  "
                    + notificationID
                    + " in the workflow "
                    + workflowID
                    + ", so this notification will not be sent.");
            continue;
          }
        }

        /*
         * Send mail using the mail domain, host and plugin specified in the
         * workflow properties file.
         */
        String ccMessage = "";
        if (!messageContext.getCCList().isEmpty()) {
          ccMessage = " , CC to " + messageContext.getCc();
        }
        log.debug(
            "Email sent to "
                + messageContext.getTo()
                + ccMessage
                + ". "
                + "Email sent by "
                + messageContext.getFrom());

        this.sendMail(request, messageContext);
      } while (tnc.moveNext()); // End Loop over mail notifications
    } catch (Exception ex) {
      log.error("Unexpected Exception " + ex + " " + ex.getMessage(), ex);
    }

    PSWorkFlowUtils.printWorkflowMessage(request, "  Exiting Method sendNotifications");
    log.debug("Exiting Method sendNotifications");
  }
  /**
   * Process the notification messages.
   *
   * @see
   *     com.percussion.extension.IPSResultDocumentProcessor#processResultDocument(java.lang.Object[],
   *     com.percussion.server.IPSRequestContext, org.w3c.dom.Document)
   */
  public Document processResultDocument(Object[] params, IPSRequestContext request, Document resDoc)
      throws PSParameterMismatchException, PSExtensionProcessingException {
    log.debug("Entering Notification");
    PSWorkFlowUtils.printWorkflowMessage(
        request, "\nNotify Assignees: enter processResultDocument ");
    int transitionID = 0;
    int toStateID = 0;
    int fromStateID = 0;
    int contentID = 0;
    int workflowID = 0;
    int revisionID = 0;
    String userName = null;
    PSWorkFlowContext wfContext = null;
    PSTransitionsContext tc = null;
    PSWorkflowRoleInfo wfRoleInfo = null;
    PSContentStatusContext csc = null;
    String contentURL = null;
    String lang = null;
    PSConnectionMgr connectionMgr = null;
    Connection connection = null;
    try {
      if (null == request) {
        throw new PSExtensionProcessingException(
            m_fullExtensionName, new IllegalArgumentException("The request must not be null"));
      }
      lang = (String) request.getSessionPrivateObject(PSI18nUtils.USER_SESSION_OBJECT_SYS_LANG);
      if (lang == null) lang = PSI18nUtils.DEFAULT_LANG;
      wfContext =
          (PSWorkFlowContext)
              request.getPrivateObject(IPSWorkFlowContext.WORKFLOW_CONTEXT_PRIVATE_OBJECT);
      if (null == wfContext) {
        log.debug("No transition, no notification sent");
        PSWorkFlowUtils.printWorkflowMessage(
            request,
            "Notify assignees: - no transition was performed - " + "no notifications will be sent");
        return resDoc;
      }
      revisionID = wfContext.getBaseRevisionNum();
      transitionID = wfContext.getTransitionID();
      if (IPSConstants.TRANSITIONID_NO_ACTION_TAKEN == transitionID
          || IPSConstants.TRANSITIONID_CHECKINOUT == transitionID) {
        PSWorkFlowUtils.printWorkflowMessage(
            request,
            "Notify assignees: - no transition was performed - " + "no notifications will be sent");
        return resDoc; // no action at all - no history
      }
      workflowID = wfContext.getWorkflowID();
      toStateID = wfContext.getStateID();
      // Note: we could get content id from workflow context
      if (null == params[0] || 0 == params[0].toString().trim().length()) {
        log.debug("no content id, no notification sent");
        return resDoc; // no content id means no notifications!
      }
      contentID = new Integer(params[0].toString()).intValue();
      if (0 == contentID) {
        log.debug("content id = 0, no notification sent");
        return resDoc; // no content id means no notifications!
      }
      if (null == params[1] || 0 == params[1].toString().trim().length()) {
        throw new PSInvalidParameterTypeException(lang, IPSExtensionErrors.EMPTY_USRNAME1);
      }
      userName = params[1].toString();
      // Get the connection
      connectionMgr = new PSConnectionMgr();
      connection = connectionMgr.getConnection();
      /*
       * get the old state id from the transition context TODO - put the old
       * state id into the workflow context, to avoid database reads.
       */
      tc = new PSTransitionsContext(transitionID, workflowID, connection);
      tc.close();
      fromStateID = tc.getTransitionFromStateID();
      wfRoleInfo =
          (PSWorkflowRoleInfo)
              request.getPrivateObject(PSWorkflowRoleInfo.WORKFLOW_ROLE_INFO_PRIVATE_OBJECT);
      if (null == wfRoleInfo) {
        throw new PSExtensionProcessingException(
            m_fullExtensionName, new PSRoleException(lang, IPSExtensionErrors.ROLEINFO_OBJ_NULL));
      }
      contentURL = PSWorkFlowUtils.getContentItemURL(contentID, revisionID, request, true);
      csc = new PSContentStatusContext(connection, contentID);
      sendNotifications(
          contentURL,
          contentID,
          workflowID,
          transitionID,
          fromStateID,
          toStateID,
          userName,
          wfRoleInfo,
          request,
          connection,
          String.valueOf(csc.getCommunityID()));

    } catch (PSNotificationSkipException se) {
      log.debug("Notification Skipped");
    } catch (PSExtensionProcessingException pe) {
      log.error(
          "Error while sending notification with user " + userName + " and contentid " + contentID,
          pe);
      throw (PSExtensionProcessingException) pe.fillInStackTrace();
    } catch (Exception e) {
      log.error(
          "Error while sending notification with user " + userName + " and contentid " + contentID,
          e);
      // error message should be improved
      String language = null;
      // if(e instanceof PSEntryNotFoundException || e instanceof
      // PSInvalidParameterTypeException )
      // {
      // language = e.getLanguageString();
      // }
      if (language == null) language = PSI18nUtils.DEFAULT_LANG;
      throw new PSExtensionProcessingException(language, m_fullExtensionName, e);
    } finally {
      if (csc != null) {
        try {
          csc.close();
        } catch (Exception ex) {
          // no-op
        }
      }
      try {
        if (null != connectionMgr) connectionMgr.releaseConnection();
      } catch (SQLException sqe) {
      }
      log.debug("End of notification");
      PSWorkFlowUtils.printWorkflowMessage(
          request, "Notify Assignees: exit processResultDocument ");
    }
    return resDoc;
  }