/*
* Copyright (c) 2009-2010 Lockheed Martin Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eurekastreams.server.service.actions.strategies;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.eurekastreams.commons.actions.TaskHandlerExecutionStrategy;
import org.eurekastreams.commons.actions.context.DefaultPrincipal;
import org.eurekastreams.commons.actions.context.PrincipalActionContext;
import org.eurekastreams.commons.actions.context.TaskHandlerActionContext;
import org.eurekastreams.commons.actions.context.service.ServiceActionContext;
import org.eurekastreams.commons.exceptions.ValidationException;
import org.eurekastreams.commons.logging.LogFactory;
import org.eurekastreams.commons.server.UserActionRequest;
import org.eurekastreams.server.action.request.notification.CreateNotificationsRequest;
import org.eurekastreams.server.action.request.notification.CreateNotificationsRequest.RequestType;
import org.eurekastreams.server.action.request.profile.SetFollowingStatusByGroupCreatorRequest;
import org.eurekastreams.server.domain.BackgroundItem;
import org.eurekastreams.server.domain.DomainGroup;
import org.eurekastreams.server.domain.Follower;
import org.eurekastreams.server.domain.Organization;
import org.eurekastreams.server.domain.Person;
import org.eurekastreams.server.domain.strategies.OrganizationHierarchyTraverser;
import org.eurekastreams.server.domain.strategies.OrganizationHierarchyTraverserBuilder;
import org.eurekastreams.server.domain.stream.StreamScope;
import org.eurekastreams.server.domain.stream.StreamScope.ScopeType;
import org.eurekastreams.server.persistence.DomainGroupMapper;
import org.eurekastreams.server.persistence.OrganizationMapper;
import org.eurekastreams.server.persistence.PersonMapper;
import org.eurekastreams.server.persistence.mappers.cache.OrganizationHierarchyCache;
/**
* Organization Creator.
*/
public class GroupCreator extends GroupPersister
{
/**
* Logger.
*/
private Log log = LogFactory.make();
/**
* Key value for organization shortName.
*/
private static final String SHORTNAME_KEY = "shortName";
/**
* Message for duplicate organization short name.
*/
private static final String DUP_SHORTNAME_MSG = "Group short name already present in system.";
/** Key value for parent organization field. */
private static final String PARENT_ORG_KEY = "orgParent";
/**
* The organization hierarchy traverser builder - needed because this class is reused by all threads, we can't share
* OrganizationHierarchyTraversers.
*/
private final OrganizationHierarchyTraverserBuilder orgTraverserBuilder;
/**
* Organization cache to look up parent orgs.
*/
private final OrganizationHierarchyCache orgHierarchyCache;
/**
* Strategy for adding group followers (coordinators are automatically added as followers/members).
*/
private TaskHandlerExecutionStrategy followStrategy;
/**
* used to lookup person creating the group.
*/
private final PersonMapper personMapper;
/**
* Constructor.
*
* @param inGroupMapper
* The Group Mapper
* @param inOrgMapper
* The org mapper
* @param inPersonMapper
* used to lookup person creating the group.
* @param inOrgTraverserBuilder
* used to update the Org statistics.
* @param inOrganizationHierarchyCache
* used to get the parent orgs.
* @param inFollowStrategy
* used to automatically add coordinators as group followers/members.
*/
public GroupCreator(final DomainGroupMapper inGroupMapper, final OrganizationMapper inOrgMapper,
final PersonMapper inPersonMapper, final OrganizationHierarchyTraverserBuilder inOrgTraverserBuilder,
final OrganizationHierarchyCache inOrganizationHierarchyCache,
final TaskHandlerExecutionStrategy inFollowStrategy)
{
super(inGroupMapper, inOrgMapper);
personMapper = inPersonMapper;
orgTraverserBuilder = inOrgTraverserBuilder;
orgHierarchyCache = inOrganizationHierarchyCache;
followStrategy = inFollowStrategy;
}
/**
* Returns DomainGroup based on id passed in inFields.
*
* @param inActionContext
* The action context.
* @param inFields
* the property map.
* @return new group.
*/
@Override
public DomainGroup get(final TaskHandlerActionContext<PrincipalActionContext> inActionContext,
final Map<String, Serializable> inFields)
{
String parentOrgShortName;
// create the group
DomainGroup group = new DomainGroup();
// set the parent org if specified (persist will catch as an error if not specified)
parentOrgShortName = inFields.get(PARENT_ORG_KEY).toString();
if (parentOrgShortName != null && !parentOrgShortName.isEmpty())
{
Organization parentOrg = getOrgMapper().findByShortName(parentOrgShortName);
group.setParentOrganization(parentOrg);
}
StreamScope groupScope = new StreamScope(ScopeType.GROUP, (String) inFields.get("shortName"));
group.setStreamScope(groupScope);
// set the capabilities as a new list to avoid search indexing problems
group.setCapabilities(new ArrayList<BackgroundItem>());
return group;
}
/**
* Persists new group object.
*
* @param inGroup
* The group.
* @param inFields
* the property map.
* @param inActionContext
* the action context
* @throws Exception
* on error
*/
@Override
public void persist(final TaskHandlerActionContext<PrincipalActionContext> inActionContext,
final Map<String, Serializable> inFields, final DomainGroup inGroup) throws Exception
{
ValidationException ve = new ValidationException();
String creatorUserName = inActionContext.getActionContext().getPrincipal().getAccountId();
// verify group has a parent org
Organization parentOrg = inGroup.getParentOrganization();
if (parentOrg == null)
{
ve.addError(PARENT_ORG_KEY, "Group must have a parent organization.");
}
// Verify that group with given short name doesn't already exist.
if (getGroupMapper().findByShortName(inGroup.getShortName()) != null)
{
ve.addError(SHORTNAME_KEY, DUP_SHORTNAME_MSG);
}
// Verify that group has on coordinator.
if (inGroup.getCoordinators().isEmpty())
{
ve.addError("coordinators", "Group must have at least one coordinator");
}
if (!ve.getErrors().isEmpty())
{
throw ve;
}
// if the groups parent organization requires approval to create groups,
// set the pending state to true.
boolean isPending = !(parentOrg.getAllUsersCanCreateGroups() || isACoordinator(parentOrg, creatorUserName));
inGroup.setPending(isPending);
// Set the current user to the createdby person.
inGroup.setCreatedBy(personMapper.findByAccountId(creatorUserName));
getGroupMapper().insert(inGroup);
if (inGroup.getParentOrganization().getCapabilities() != null)
{
inGroup.getParentOrganization().getCapabilities().size();
}
// sets the destination entity id for the group's stream scope
inGroup.getStreamScope().setDestinationEntityId(inGroup.getId());
OrganizationHierarchyTraverser orgTraverser = orgTraverserBuilder.getOrganizationHierarchyTraverser();
orgTraverser.traverseHierarchy(inGroup);
getOrgMapper().updateOrganizationStatistics(orgTraverser);
queueAsyncAction(inActionContext, inGroup, false);
// Make all coordinators follow/join the new group
for (Person coordinator : inGroup.getCoordinators())
{
SetFollowingStatusByGroupCreatorRequest currentRequest = new SetFollowingStatusByGroupCreatorRequest(
coordinator.getId(), inGroup.getId(), Follower.FollowerStatus.FOLLOWING, inGroup.getName(),
isPending);
ServiceActionContext currentContext = new ServiceActionContext(currentRequest, new DefaultPrincipal(
creatorUserName, inActionContext.getActionContext().getPrincipal().getOpenSocialId(),
inActionContext.getActionContext().getPrincipal().getId()));
TaskHandlerActionContext<PrincipalActionContext> currentTaskHandlerActionContext =
// line break
new TaskHandlerActionContext<PrincipalActionContext>(currentContext, inActionContext
.getUserActionRequests());
followStrategy.execute(currentTaskHandlerActionContext);
}
// trigger notification if group will be pending approval
if (isPending)
{
CreateNotificationsRequest request = new CreateNotificationsRequest(RequestType.REQUEST_NEW_GROUP,
inActionContext.getActionContext().getPrincipal().getId(), parentOrg.getId(), inGroup.getId());
inActionContext.getUserActionRequests().add(
new UserActionRequest("createNotificationsAction", null, request));
}
}
// TODO remove this if we refactor to change the domains org object's
// isCoordinator method to recursively look.
/**
* Get whiter the user is a coordinator for this org or it's sub orgs.
*
* @param org
* The org object you are currently on.
* @param accountId
* The account ID of the person you want to check.
* @return a boolean is the account ID is a coordinator of this org or any of it's parents.
*/
public boolean isACoordinator(final Organization org, final String accountId)
{
boolean isCoordinator = org.isCoordinator(accountId);
for (Long subOrgId : orgHierarchyCache.getParentOrganizations(org.getId()))
{
if (isCoordinator)
{
break;
}
isCoordinator = getOrgMapper().findById(subOrgId).isCoordinator(accountId);
}
return isCoordinator;
}
}