/**
   * Gets a new person.
   *
   * @param inActionContext the action context
   * @param inFields the fields.
   * @return a new person.
   */
  @Override
  public Person get(
      final TaskHandlerActionContext<PrincipalActionContext> inActionContext,
      final Map<String, Serializable> inFields) {
    // create the person
    Person person =
        new Person(
            (String) inFields.get("accountId"),
            (String) inFields.get("firstName"),
            (String) inFields.get("middleName"),
            (String) inFields.get("lastName"),
            (String) inFields.get("preferredName"));
    person.setEmail((String) inFields.get("email"));
    person.setOpenSocialId(UUID.randomUUID().toString());
    person.setCompanyName((String) inFields.get("companyName"));

    // create and add start page tabs
    TabGroup startTabGroup = new TabGroup();
    for (String tabType : startPageTabs) {
      // These tabs create their own templates based on other templates.
      TabTemplate template = new TabTemplate(tabMapper.getTabTemplate(tabType));
      for (Gadget gadget : template.getGadgets()) {
        gadget.setOwner(person);
      }
      startTabGroup.addTab(new Tab(template));
    }
    person.setStartTabGroup(startTabGroup);

    // Make the default view for a person
    StreamScope personScope = new StreamScope(ScopeType.PERSON, (String) inFields.get("accountId"));

    person.setStreamScope(personScope);

    Set<StreamScope> defaultScopeList = new HashSet<StreamScope>();
    defaultScopeList.add(personScope);

    List<Stream> streams = getStreamsForPerson();

    person.setStreams(streams);

    // Set hidden line indexes.
    person.setStreamViewHiddenLineIndex(streams.size() - 1);
    person.setGroupStreamHiddenLineIndex(3);

    if (inFields.containsKey("additionalProperties")) {
      HashMap<String, String> additionalProperties =
          (HashMap<String, String>) inFields.get("additionalProperties");
      person.setAdditionalProperties(additionalProperties);
    }

    // remove public settable properties already handled from map so updater
    // doesn't do them again.
    inFields.remove("organization");
    inFields.remove("email");

    return person;
  }
  /**
   * Persists a new person and make them follow themselves.
   *
   * @param inActionContext the action context
   * @param inFields the fields.
   * @param inPerson the person to persist.
   * @throws Exception On error.
   */
  @Override
  public void persist(
      final TaskHandlerActionContext<PrincipalActionContext> inActionContext,
      final Map<String, Serializable> inFields,
      final Person inPerson)
      throws Exception {
    personMapper.insert(inPerson);

    // sets the destination entity id for the person's stream scope
    inPerson.getStreamScope().setDestinationEntityId(inPerson.getId());

    // this has to be the last thing we do, since it updates the person behind the back of the
    // object model
    personMapper.addFollower(inPerson.getId(), inPerson.getId());
  }
  /**
   * Test get followers. Dataset.xml has one follower for the group, so we should start with 1 and
   * then see 3.
   */
  @Test
  public void testGetFollowers() {
    final int maxFollowers = 10;

    DomainGroup group = jpaGroupMapper.findByShortName("group1");
    Person fordp2 = jpaPersonMapper.findByAccountId("fordp2");
    Person csagan = jpaPersonMapper.findByAccountId("csagan");

    PagedSet<Person> followers = jpaGroupMapper.getFollowers("group1", 0, maxFollowers);

    assertEquals(3, followers.getTotal());

    jpaGroupMapper.addFollower(fordp2.getId(), group.getId());
    jpaGroupMapper.addFollower(csagan.getId(), group.getId());

    followers = jpaGroupMapper.getFollowers("group1", 0, maxFollowers);

    assertEquals(5, followers.getTotal());
  }
  /**
   * Perform security check on this action to verify user can execute. Throw appropriate
   * AuthorizationException in needed.
   *
   * @param inActionContext {@link PrincipalActionContext}.
   * @throws AuthorizationException thrown if the user does not have proper access
   */
  @Override
  public void authorize(final PrincipalActionContext inActionContext)
      throws AuthorizationException {
    AddGadgetRequest request = (AddGadgetRequest) inActionContext.getParams();
    Long tabId = request.getTabId();

    // get the Tab we're inserting the Gadget into
    if (tabId == null) {
      // if client passes null for id, find the first tab
      Person person = personMapper.findByAccountId(inActionContext.getPrincipal().getAccountId());
      tabId = person.getStartTabGroup().getTabs().get(0).getId();
    }

    // This will throw AuthorizationException if user doesn't have permissions.
    if (!tabPermission.canModifyGadgets(
        inActionContext.getPrincipal().getAccountId(), tabId, true)) {
      throw new AuthorizationException("Failed to authorize adding of the supplied gadget.");
    }
  }
  /**
   * 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;
  }
  @Override
  public Serializable execute(final PrincipalActionContext inActionContext)
      throws ExecutionException {
    SetTabOrderRequest currentRequest = (SetTabOrderRequest) inActionContext.getParams();

    Person person = personMapper.findByAccountId(inActionContext.getPrincipal().getAccountId());

    List<Tab> tabs = person.getTabs(currentRequest.getTabType());

    // Find the tab to be moved
    int oldIndex = findTabIndex(tabs, currentRequest.getTabId());

    Tab movingTab = tabs.get(oldIndex);

    // move the tab
    tabs.remove(oldIndex);
    tabs.add(currentRequest.getNewIndex(), movingTab);

    personMapper.flush();

    return Boolean.TRUE;
  }
  /** Test addFollower when the person's already following the group. */
  @Test
  public void testAddFollowerWhenAlreadyFollowing() {
    final long personId = 99L;
    final long groupId = 1L;

    Person p = jpaPersonMapper.findById(personId);
    DomainGroup g = jpaGroupMapper.findById(groupId);

    int initialGroupsCount = p.getGroupCount();
    int initialFollowersCount = g.getFollowersCount();

    // invoke SUT
    jpaGroupMapper.addFollower(p.getId(), g.getId());

    // clear the entity manager, reload the entities, and assert the counts haven't changed
    getEntityManager().clear();

    p = jpaPersonMapper.findById(personId);
    g = jpaGroupMapper.findById(groupId);

    assertEquals(initialGroupsCount, p.getGroupCount());
    assertEquals(initialFollowersCount, g.getFollowersCount());
  }
  /** Test remove follower. */
  @Test
  @Transactional
  public void testRemoveFollower() {
    DomainGroup group = jpaGroupMapper.findByShortName("group1");
    Person burns = jpaPersonMapper.findByAccountId("mrburns");

    assertEquals(0, burns.getFollowingCount());
    assertEquals(1, burns.getGroupCount());
    assertEquals(3, group.getFollowersCount());
    assertTrue(jpaGroupMapper.isFollowing("mrburns", "group1"));

    jpaGroupMapper.removeFollower(burns.getId(), group.getId());
    getEntityManager().clear();
    group = jpaGroupMapper.findByShortName("group1");
    burns = jpaPersonMapper.findByAccountId("mrburns");

    assertEquals(0, burns.getFollowingCount());
    assertEquals(0, burns.getGroupCount());
    assertEquals(2, group.getFollowersCount());
    assertFalse(jpaGroupMapper.isFollowing("mrburns", "group1"));
  }
  /** Test add follower. */
  @Test
  public void testAddFollower() {
    DomainGroup group = jpaGroupMapper.findByShortName("group1");

    Person fordp2 = jpaPersonMapper.findByAccountId("fordp2");
    Person csagan = jpaPersonMapper.findByAccountId("csagan");

    // Verify initial state
    assertFalse(jpaGroupMapper.isFollowing("fordp2", "group1"));
    assertFalse(jpaGroupMapper.isFollowing("csagan", "group1"));
    assertEquals(0, fordp2.getFollowingCount());
    assertEquals(0, fordp2.getGroupCount());
    assertEquals(0, csagan.getFollowingCount());
    assertEquals(1, csagan.getGroupCount());

    // ford wants to follow the group
    jpaGroupMapper.addFollower(fordp2.getId(), group.getId());

    getEntityManager().clear();

    fordp2 = jpaPersonMapper.findByAccountId("fordp2");
    csagan = jpaPersonMapper.findByAccountId("csagan");

    // verify new state
    assertTrue(jpaGroupMapper.isFollowing("fordp2", "group1"));
    // Test case insensitivity (everything should be lower cased by the mapper).
    assertTrue(jpaGroupMapper.isFollowing("fordp2", "Group1"));
    assertFalse(jpaGroupMapper.isFollowing("csagan", "group1"));
    assertEquals(0, fordp2.getFollowingCount());
    assertEquals(1, fordp2.getGroupCount());
    assertEquals(0, csagan.getFollowingCount());
    assertEquals(1, csagan.getGroupCount());

    // csagan wants to follow the group
    jpaGroupMapper.addFollower(csagan.getId(), group.getId());

    getEntityManager().clear();

    fordp2 = jpaPersonMapper.findByAccountId("fordp2");
    csagan = jpaPersonMapper.findByAccountId("csagan");

    // verify new state
    assertTrue(jpaGroupMapper.isFollowing("fordp2", "group1"));
    assertTrue(jpaGroupMapper.isFollowing("csagan", "group1"));
    // and verify csagan's counts after the change
    assertEquals(0, csagan.getFollowingCount());
    assertEquals(2, csagan.getGroupCount());
  }