/** Action to send notification emails. */
public class SendNotificationEmailExecution implements ExecutionStrategy<ActionContext> {
  /** Log. */
  private static Logger log = LoggerFactory.getLogger(LogFactory.getClassName());

  /** For sending email. */
  private final EmailerFactory emailer;

  /**
   * Constructor.
   *
   * @param inEmailer For sending email.
   */
  public SendNotificationEmailExecution(final EmailerFactory inEmailer) {
    emailer = inEmailer;
  }

  /** {@inheritDoc} */
  @Override
  public Serializable execute(final ActionContext inActionContext) throws ExecutionException {
    NotificationEmailDTO request = (NotificationEmailDTO) inActionContext.getParams();
    try {
      MimeMessage message = emailer.createMessage();

      emailer.setSubject(message, request.getSubject());
      emailer.setTextBody(message, request.getTextBody());
      emailer.setHtmlBody(message, request.getHtmlBody());
      if (StringUtils.isNotBlank(request.getToRecipient())) {
        emailer.setTo(message, request.getToRecipient());
      }
      if (StringUtils.isNotBlank(request.getBccRecipients())) {
        emailer.setBcc(message, request.getBccRecipients());
      }
      if (StringUtils.isNotBlank(request.getReplyTo())) {
        emailer.setReplyTo(message, request.getReplyTo());
      }
      if (request.isHighPriority()) {
        message.addHeader("Importance", "high");
        message.addHeader("X-Priority", "1");
      }

      emailer.sendMail(message);

      log.debug("Sent email for notification {}", request.getDescription());
    } catch (MessagingException ex) {
      String msg = "Failed to send email message for notification " + request.getDescription();
      log.error(msg, ex);
      throw new ExecutionException(msg, ex);
    }

    return null;
  }
}
/** Delete a bookmark from current user's collection. */
public class DeleteCurrentUserBookmarkExecution
    implements ExecutionStrategy<PrincipalActionContext> {

  /** Logger. */
  private Log log = LogFactory.make();

  /** Find by id mapper. */
  private FindByIdMapper<Person> personMapper;

  /**
   * Constructor.
   *
   * @param inPersonMapper Find by id mapper.
   */
  public DeleteCurrentUserBookmarkExecution(final FindByIdMapper<Person> inPersonMapper) {
    personMapper = inPersonMapper;
  }

  /**
   * Delete a bookmark from current user's collection.
   *
   * @param inActionContext action context.
   * @return true upon successful completion.
   */
  @Override
  public Serializable execute(final PrincipalActionContext inActionContext) {
    Person person =
        personMapper.execute(new FindByIdRequest("Person", inActionContext.getPrincipal().getId()));
    Long ssIdToRemove = (Long) inActionContext.getParams();
    List<StreamScope> bookmarks = person.getBookmarks();

    log.debug("Attempting to delete bookmark with id: " + ssIdToRemove);

    for (StreamScope ss : bookmarks) {
      log.debug("Examinging SS with id: " + ss.getId());
      if (ss.getId() == ssIdToRemove) {
        log.debug("Match found!");
        bookmarks.remove(ss);
        personMapper.flush();
        break;
      }
    }

    return Boolean.TRUE;
  }
}
/**
 * Cache mapper to build and populate the input org child's parent organization with a skeleton Org
 * that should be sufficient for most client-side uses.
 */
public class PopulateOrgChildWithSkeletonParentOrgsCacheMapper {
  /** Log. */
  Log log = LogFactory.make();

  /** Mapper to get people by ids cached mappers. */
  private GetOrganizationsByIds getOrgsByIdsCacheMapper;

  /**
   * Constructor.
   *
   * @param inGetOrgsByIdsCacheMapper mapper to get a cached organization
   */
  public PopulateOrgChildWithSkeletonParentOrgsCacheMapper(
      final GetOrganizationsByIds inGetOrgsByIdsCacheMapper) {
    getOrgsByIdsCacheMapper = inGetOrgsByIdsCacheMapper;
  }

  /**
   * Populate the input child org with a skeleton Organization.
   *
   * @param inOrgChild the org child to populate
   */
  public void populateParentOrgSkeleton(final OrganizationChild inOrgChild) {
    ArrayList<OrganizationChild> orgChildren = new ArrayList<OrganizationChild>();
    orgChildren.add(inOrgChild);
    populateParentOrgSkeletons(orgChildren);
  }

  /**
   * Populate the input org child's parent organization with a skeleton Org that should be
   * sufficient for most client-side uses.
   *
   * <p>- orgId, shortName, name, bannerId, backgroundColor
   *
   * @param inOrgChildren the org children to populate skeleton orgs for
   */
  public void populateParentOrgSkeletons(final Collection<OrganizationChild> inOrgChildren) {
    // build up a list of org ids to fetch
    List<Long> orgIds = new ArrayList<Long>();
    for (OrganizationChild oc : inOrgChildren) {
      if (oc.getParentOrgId() != null
          && oc.getParentOrgId() > 0
          && !orgIds.contains(oc.getParentOrgId())) {
        log.trace("queuing org id for cache retrieval: " + oc.getParentOrgId());
        orgIds.add(oc.getParentOrgId());
      }
    }

    // batch get
    List<OrganizationModelView> orgMvs = getOrgsByIdsCacheMapper.execute(orgIds);

    // hash group them for quick access
    HashMap<Long, OrganizationModelView> hash = new HashMap<Long, OrganizationModelView>();
    for (OrganizationModelView orgMv : orgMvs) {
      hash.put(orgMv.getEntityId(), orgMv);
    }

    for (OrganizationChild oc : inOrgChildren) {
      if (oc.getParentOrgId() != null && oc.getParentOrgId() > 0) {
        OrganizationModelView orgMv = hash.get(oc.getParentOrgId());
        log.info("Setting parent org id skeleton " + oc.toString() + " to " + orgMv.toString());
        oc.setParentOrganization(new Organization(orgMv));
      }
    }
  }
}
/** Action to get the followers of a person. */
public class GetFollowingExecution implements ExecutionStrategy<PrincipalActionContext> {
  /** Logger. */
  private final Log log = LogFactory.make();

  /** Mapper to find the follower's id given their account id. */
  private final DomainMapper<String, Long> getPersonIdByAccountIdMapper;

  /** Mapper returning the ids of the entities being followed. */
  private final DomainMapper<Long, List<Long>> idsMapper;

  /** Mapper returning entities (people, groups) given their ids. */
  private final DomainMapper<List<Long>, List<FollowerStatusable>> bulkModelViewMapper;

  /** Populator for follower status of results. */
  private final FollowerStatusPopulator<FollowerStatusable> followerStatusPopulator;

  /**
   * Constructor.
   *
   * @param inGetPersonIdByAccountIdMapper Mapper to find the follower's id given their account id.
   * @param inIdsMapper Mapper returning the ids of the entities being followed.
   * @param inBulkModelViewMapper Mapper returning entities (people, groups) given their ids.
   * @param inFollowerStatusPopulator populates each PersonModelView with the current user's follow
   *     status
   */
  public GetFollowingExecution(
      final DomainMapper<String, Long> inGetPersonIdByAccountIdMapper,
      final DomainMapper<Long, List<Long>> inIdsMapper,
      final DomainMapper<List<Long>, List<FollowerStatusable>> inBulkModelViewMapper,
      final FollowerStatusPopulator<FollowerStatusable> inFollowerStatusPopulator) {
    getPersonIdByAccountIdMapper = inGetPersonIdByAccountIdMapper;
    idsMapper = inIdsMapper;
    bulkModelViewMapper = inBulkModelViewMapper;
    followerStatusPopulator = inFollowerStatusPopulator;
  }

  /**
   * Returns Set of people following a user excluding themselves.
   *
   * @param inActionContext The action context.
   * @return true if the group exists and the user is authorized, false otherwise
   */
  @Override
  public PagedSet<FollowerStatusable> execute(final PrincipalActionContext inActionContext) {
    // get the request
    GetFollowersFollowingRequest inRequest =
        (GetFollowersFollowingRequest) inActionContext.getParams();

    // get the unique entity Id
    final String entityUniqueId = inRequest.getEntityId();

    final long currentUserId = inActionContext.getPrincipal().getId();

    Long entityId = getPersonIdByAccountIdMapper.execute(entityUniqueId);

    List<Long> allIds = idsMapper.execute(entityId);

    // determine the page
    int startIndex = (inRequest.getStartIndex()).intValue();
    int endIndex = (inRequest.getEndIndex()).intValue();

    PagedSet<FollowerStatusable> pagedSet;
    if (allIds.isEmpty()) {
      pagedSet = new PagedSet<FollowerStatusable>();
    } else if (startIndex >= allIds.size()) {
      // if asking for a range beyond the end of the list return an empty set
      pagedSet = new PagedSet<FollowerStatusable>();
      pagedSet.setTotal(allIds.size());
    } else {
      if (endIndex >= allIds.size()) {
        endIndex = allIds.size() - 1;
      }
      List<Long> pageIds = allIds.subList(startIndex, endIndex + 1);

      List<FollowerStatusable> list = bulkModelViewMapper.execute(pageIds);
      followerStatusPopulator.execute(currentUserId, list, FollowerStatus.NOTSPECIFIED);

      pagedSet = new PagedSet<FollowerStatusable>(startIndex, endIndex, allIds.size(), list);
    }

    if (log.isTraceEnabled()) {
      log.trace(
          "Retrieved "
              + pagedSet.getFromIndex()
              + " to "
              + pagedSet.getToIndex()
              + " of "
              + pagedSet.getTotal()
              + " following for person "
              + entityUniqueId);
    }

    return pagedSet;
  }
}
/** Mapper that gets the flagged activities pertaining to an org. */
public class GetFlaggedActivities
    extends BaseArgDomainMapper<GetFlaggedActivitiesRequest, PagedSet<ActivityDTO>> {
  /** Log. */
  private Log log = LogFactory.make();

  /** Bulk activities mapper. */
  private DomainMapper<List<Long>, List<ActivityDTO>> activitiesMapper;

  /**
   * Constructor.
   *
   * @param inActivitiesMapper the activities mapper
   */
  public GetFlaggedActivities(
      final DomainMapper<List<Long>, List<ActivityDTO>> inActivitiesMapper) {
    activitiesMapper = inActivitiesMapper;
  }

  /**
   * Get all flagged activities.
   *
   * @param inRequest the request containing the start and end index
   * @return a paged set of ActivityDTOs
   */
  @Override
  @SuppressWarnings("unchecked")
  public PagedSet<ActivityDTO> execute(final GetFlaggedActivitiesRequest inRequest) {
    if (inRequest.getRequestingUserAccountId() == null) {
      log.error("Missing requesting user account.");
      throw new RuntimeException("Missing requesting user account.");
    }

    // get the total number of flagged activities
    Long flaggedActivityCount = (Long) buildQuery("count(*)").getSingleResult();

    List<ActivityDTO> activities;
    if (flaggedActivityCount > 0) {
      log.info(
          "Found "
              + flaggedActivityCount
              + " flagged activity ids - passing them to the bulk activity mapper for ActivityDTOs.");

      Query q = buildQuery("id");
      q.setFirstResult(inRequest.getStartIndex());
      q.setMaxResults(inRequest.getEndIndex() - inRequest.getStartIndex() + 1);
      List<Long> activityIds = q.getResultList();

      activities = activitiesMapper.execute(activityIds);
    } else {
      log.info("Found no flagged activity ids.");
      activities = new ArrayList<ActivityDTO>();
    }
    return new PagedSet<ActivityDTO>(
        inRequest.getStartIndex(),
        inRequest.getEndIndex(),
        flaggedActivityCount.intValue(),
        activities);
  }

  /**
   * Build a query for selecting field(s) from flagged activities.
   *
   * @param fieldsString a comma-separated string listing the fields to select
   * @return a Query object
   */
  private Query buildQuery(final String fieldsString) {
    return getEntityManager()
        .createQuery("SELECT " + fieldsString + " FROM Activity WHERE flagged = :isFlagged")
        .setParameter("isFlagged", true);
  }
}