/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Cyclos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package nl.strohalm.cyclos.webservices.utils;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import nl.strohalm.cyclos.entities.Relationship;
import nl.strohalm.cyclos.entities.access.Channel;
import nl.strohalm.cyclos.entities.access.ChannelPrincipal;
import nl.strohalm.cyclos.entities.access.MemberUser;
import nl.strohalm.cyclos.entities.access.PrincipalType;
import nl.strohalm.cyclos.entities.customization.fields.CustomField;
import nl.strohalm.cyclos.entities.customization.fields.MemberCustomField;
import nl.strohalm.cyclos.entities.customization.fields.MemberCustomFieldValue;
import nl.strohalm.cyclos.entities.exceptions.EntityNotFoundException;
import nl.strohalm.cyclos.entities.groups.Group.Status;
import nl.strohalm.cyclos.entities.groups.GroupFilter;
import nl.strohalm.cyclos.entities.groups.MemberGroup;
import nl.strohalm.cyclos.entities.members.Element;
import nl.strohalm.cyclos.entities.members.FullTextMemberQuery;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.entities.members.MemberQuery;
import nl.strohalm.cyclos.entities.services.ServiceClient;
import nl.strohalm.cyclos.services.access.AccessServiceLocal;
import nl.strohalm.cyclos.services.customization.MemberCustomFieldServiceLocal;
import nl.strohalm.cyclos.services.elements.ElementServiceLocal;
import nl.strohalm.cyclos.services.groups.GroupServiceLocal;
import nl.strohalm.cyclos.services.permissions.PermissionServiceLocal;
import nl.strohalm.cyclos.services.services.ServiceClientServiceLocal;
import nl.strohalm.cyclos.utils.CustomFieldHelper;
import nl.strohalm.cyclos.utils.EntityHelper;
import nl.strohalm.cyclos.utils.access.LoggedUser;
import nl.strohalm.cyclos.utils.conversion.Transformer;
import nl.strohalm.cyclos.webservices.PrincipalParameters;
import nl.strohalm.cyclos.webservices.WebServiceContext;
import nl.strohalm.cyclos.webservices.WebServiceFaultsEnum;
import nl.strohalm.cyclos.webservices.members.FullTextMemberSearchParameters;
import nl.strohalm.cyclos.webservices.members.MemberResultPage;
import nl.strohalm.cyclos.webservices.members.MemberSearchParameters;
import nl.strohalm.cyclos.webservices.members.RegisterMemberParameters;
import nl.strohalm.cyclos.webservices.model.FieldValueVO;
import nl.strohalm.cyclos.webservices.model.MemberVO;
import nl.strohalm.cyclos.webservices.model.MyProfileVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
/**
* Utility class for members<br>
* <b>WARN</b>: Be aware that this helper <b>doesn't</b> access the services through the security layer. They are all local services.
* @author luis
*/
public class MemberHelper {
private QueryHelper queryHelper;
private ImageHelper imageHelper;
private FieldHelper fieldHelper;
private ElementServiceLocal elementServiceLocal;
private ServiceClientServiceLocal serviceClientServiceLocal;
private MemberCustomFieldServiceLocal memberCustomFieldServiceLocal;
private PermissionServiceLocal permissionService;
private GroupServiceLocal groupServiceLocal;
private ChannelHelper channelHelper;
private AccessServiceLocal accessServiceLocal;
private CustomFieldHelper customFieldHelper;
/**
* Throws an invalid-channel fault if the current client's channel is not enabled for the given member
*/
public void checkChannelEnabledForMember(final Member member) {
if (!isChannelEnabledForMember(member)) {
throw WebServiceHelper.fault(WebServiceFaultsEnum.INVALID_CHANNEL);
}
}
/**
* Returns whether the current client's channel is enabled for the given member
*/
public boolean isChannelEnabledForMember(final Member member) {
final Channel channel = WebServiceContext.getChannel();
// If not restricted to a channel, no need to check
if (channel == null) {
return true;
}
// If the channel restricted member is the same member, no need to check
final Member restrictedMember = WebServiceContext.getMember();
if (restrictedMember != null && restrictedMember.equals(member)) {
return true;
}
return accessServiceLocal.isChannelEnabledForMember(channel.getInternalName(), member);
}
/**
* Loads a member using the given principal, according to the current channel
*/
public Member loadByPrincipal(PrincipalType principalType, final String principal, final Relationship... relationships) {
if (StringUtils.isNotEmpty(principal)) {
final Channel channel = WebServiceContext.getChannel();
if (channel != null) {
if (principalType == null) {
principalType = channel.getDefaultPrincipalType();
} else if (!channel.getPrincipalTypes().contains(principalType)) {
return null;
}
}
Member member;
if (ArrayUtils.isNotEmpty(relationships)) {
Relationship[] tmp = new Relationship[relationships.length + 2];
tmp[0] = Element.Relationships.USER;
tmp[1] = Element.Relationships.GROUP;
System.arraycopy(relationships, 0, tmp, 2, relationships.length);
member = elementServiceLocal.loadByPrincipal(principalType, principal, tmp);
} else {
member = elementServiceLocal.loadByPrincipal(principalType, principal, Element.Relationships.USER, Element.Relationships.GROUP);
}
if (!permissionService.relatesTo(member)) {
throw new EntityNotFoundException(Member.class);
}
return member;
}
return null;
}
/**
* Loads a member using the given principal, according to the current channel
*/
public Member loadByPrincipal(final String principalType, final String principal, final Relationship... relationships) {
final PrincipalType type = channelHelper.resolvePrincipalType(principalType);
return loadByPrincipal(type, principal, relationships);
}
/**
* Checks the {@link ServiceClient#getMember()}. If restricted to a member, returns it, ignoring the received id. Otherwise, loads the member with
* the given principal
*/
public Member resolveMember(final PrincipalParameters params) {
if (params == null) {
return null;
}
return resolveMember(params.getPrincipalType(), params.getPrincipal());
}
/**
* Checks the {@link ServiceClient#getMember()}. If restricted to a member, returns it, ignoring the received id. Otherwise, loads the member with
* the given principal
*/
public Member resolveMember(final PrincipalType principalType, final String principal) {
final Member member = WebServiceContext.getMember();
if (member == null) {
if (StringUtils.isEmpty(principal)) {
return null;
}
return loadByPrincipal(principalType, principal);
} else {
return member;
}
}
/**
* Checks the {@link ServiceClient#getMember()}. If restricted to a member, returns it, ignoring the received id. Otherwise, loads the member with
* the given principal
*/
public Member resolveMember(final String principalType, final String principal) {
final PrincipalType type = channelHelper.resolvePrincipalType(principalType);
return resolveMember(type, principal);
}
/**
* Invokes resolveMember(String), returning it's user, or null
*/
public MemberUser resolveUser(final PrincipalType principalType, final String principal) {
final Member member = resolveMember(principalType, principal);
return member == null ? null : member.getMemberUser();
}
public void setAccessServiceLocal(final AccessServiceLocal accessService) {
accessServiceLocal = accessService;
}
public void setChannelHelper(final ChannelHelper channelHelper) {
this.channelHelper = channelHelper;
}
public void setCustomFieldHelper(final CustomFieldHelper customFieldHelper) {
this.customFieldHelper = customFieldHelper;
}
public void setElementServiceLocal(final ElementServiceLocal elementService) {
elementServiceLocal = elementService;
}
public void setFieldHelper(final FieldHelper fieldHelper) {
this.fieldHelper = fieldHelper;
}
public void setGroupServiceLocal(final GroupServiceLocal groupService) {
groupServiceLocal = groupService;
}
public void setImageHelper(final ImageHelper imageHelper) {
this.imageHelper = imageHelper;
}
public void setMemberCustomFieldServiceLocal(final MemberCustomFieldServiceLocal memberCustomFieldService) {
memberCustomFieldServiceLocal = memberCustomFieldService;
}
public void setPermissionServiceLocal(final PermissionServiceLocal permissionService) {
this.permissionService = permissionService;
}
public void setQueryHelper(final QueryHelper queryHelper) {
this.queryHelper = queryHelper;
}
public void setServiceClientServiceLocal(final ServiceClientServiceLocal serviceClientService) {
serviceClientServiceLocal = serviceClientService;
}
public FullTextMemberQuery toFullTextQuery(final FullTextMemberSearchParameters params) {
if (params == null) {
return null;
}
final FullTextMemberQuery query = new FullTextMemberQuery();
query.setEnabled(true);
query.fetch(Element.Relationships.GROUP, Element.Relationships.USER);
if (params.getShowCustomFields()) {
query.fetch(Member.Relationships.CUSTOM_VALUES);
}
if (params.getShowImages()) {
query.fetch(Member.Relationships.IMAGES);
}
query.setKeywords(params.getKeywords());
final GroupFilter[] groupFilters = EntityHelper.references(GroupFilter.class, params.getGroupFilterIds());
if (groupFilters == null || groupFilters.length > 0) {
query.setGroupFilters(Arrays.asList(groupFilters));
}
final MemberGroup[] groups = EntityHelper.references(MemberGroup.class, params.getGroupIds());
if (groups == null || groups.length > 0) {
query.setGroups(Arrays.asList(groups));
}
queryHelper.fill(params, query);
if (params.getExcludeLoggedIn()) {
Member member = LoggedUser.member();
query.setExcludeElements(Collections.<Element> singleton(member));
}
query.setWithImagesOnly(params.getWithImagesOnly());
List<MemberCustomField> memberFields = customFieldHelper.onlyForMemberSearch(memberCustomFieldServiceLocal.list());
query.setCustomValues(customFieldHelper.toValueCollection(memberFields, params.getFields()));
return query;
}
/**
* Converts a member to VO with all custom fields and images
*/
public MemberVO toFullVO(final Member member) {
if (!member.isActive() || member.getGroup().getStatus() == Status.REMOVED) {
throw new EntityNotFoundException();
}
List<MemberCustomField> fields = memberCustomFieldServiceLocal.list();
if (!LoggedUser.isUnrestrictedClient()) {
MemberGroup group = LoggedUser.member().getMemberGroup();
fields = customFieldHelper.onlyVisibleFields(fields, group);
}
return toVO(member, fields, true);
}
public Member toMember(final RegisterMemberParameters params) {
// Find the group
MemberGroup group;
try {
final ServiceClient client = serviceClientServiceLocal.load(WebServiceContext.getClient().getId(), ServiceClient.Relationships.MANAGE_GROUPS);
final Set<MemberGroup> manageGroups = client.getManageGroups();
final Long groupId = params.getGroupId();
if (groupId == null || groupId.intValue() <= 0) {
group = manageGroups.iterator().next();
} else {
group = groupServiceLocal.load(groupId);
if (!manageGroups.contains(group)) {
throw new Exception();
}
}
} catch (final Exception e) {
throw new EntityNotFoundException();
}
final MemberUser user = new MemberUser();
user.setUsername(params.getUsername());
final Member member = new Member();
member.setGroup(group);
member.setUser(user);
member.setName(params.getName());
member.setEmail(params.getEmail());
List<MemberCustomField> fields = memberCustomFieldServiceLocal.list();
fields = customFieldHelper.onlyForGroup(fields, group);
final Collection<MemberCustomFieldValue> fieldValues = customFieldHelper.toValueCollection(fields, params.getFields());
member.setCustomValues(fieldValues);
return member;
}
/**
* Converts the given member into a {@link MyProfileVO}
*/
public MyProfileVO toMyProfileVO(final Member member) {
if (!member.isActive() || member.getGroup().getStatus() == Status.REMOVED) {
throw new EntityNotFoundException();
}
List<MemberCustomField> fields = customFieldHelper.onlyOwnedFields(memberCustomFieldServiceLocal.list(), member.getMemberGroup());
// Convert to VO
MyProfileVO vo = toVO(MyProfileVO.class, member, fields, fields, true);
// Find out the hidden fields
Set<String> hiddenFields = new HashSet<String>();
for (MemberCustomFieldValue fieldValue : member.getCustomValues()) {
CustomField field = fieldValue.getField();
if (fields.contains(field) && fieldValue.isHidden()) {
hiddenFields.add(field.getInternalName());
}
}
vo.setHiddenFields(hiddenFields);
return vo;
}
public MemberQuery toQuery(final MemberSearchParameters params) {
if (params == null) {
return null;
}
final MemberQuery query = new MemberQuery();
query.setEnabled(true);
query.fetch(Element.Relationships.GROUP, Element.Relationships.USER);
if (params.getShowCustomFields()) {
query.fetch(Member.Relationships.CUSTOM_VALUES);
}
if (params.getShowImages()) {
query.fetch(Member.Relationships.IMAGES);
}
final GroupFilter[] groupFilters = EntityHelper.references(GroupFilter.class, params.getGroupFilterIds());
if (groupFilters == null || groupFilters.length > 0) {
query.setGroupFilters(Arrays.asList(groupFilters));
}
final MemberGroup[] groups = EntityHelper.references(MemberGroup.class, params.getGroupIds());
if (groups == null || groups.length > 0) {
query.setGroups(Arrays.asList(groups));
}
query.setUsername(params.getUsername());
query.setName(params.getName());
query.setEmail(params.getEmail());
query.setWithImagesOnly(params.getWithImagesOnly());
query.setRandomOrder(params.getRandomOrder());
if (params.getExcludeLoggedIn() && LoggedUser.isMember()) {
query.setExcludeElements(Collections.singleton(LoggedUser.element()));
}
queryHelper.fill(params, query);
final List<FieldValueVO> fields = params.getFields();
if (fields != null && fields.size() > 0) {
query.setCustomValues(customFieldHelper.toValueCollection(memberCustomFieldServiceLocal.list(), fields));
}
return query;
}
public MemberResultPage toResultPage(final List<Member> members, final boolean useCustomFields, final boolean useImages) {
final List<MemberCustomField> allFields = memberCustomFieldServiceLocal.list();
final Map<MemberGroup, List<MemberCustomField>> fieldsByGroup = new HashMap<MemberGroup, List<MemberCustomField>>();
return queryHelper.toResultPage(MemberResultPage.class, members, new Transformer<Member, MemberVO>() {
@Override
public MemberVO transform(final Member member) {
MemberGroup memberGroup = member.getMemberGroup();
List<MemberCustomField> fields = null;
if (useCustomFields) {
fields = fieldsByGroup.get(memberGroup);
if (fields == null) {
fields = customFieldHelper.onlyForGroup(allFields, memberGroup);
fieldsByGroup.put(memberGroup, fields);
}
}
return toVO(member, fields, useImages);
}
});
}
/**
* Converts a member to VO, with minimum details
*/
public MemberVO toVO(final Member member) {
return toVO(member, null, false);
}
/**
* Converts a member to VO with customized details
*/
public MemberVO toVO(final Member member, final Collection<MemberCustomField> fields, final Collection<MemberCustomField> requiredFields, final boolean useImages) {
return toVO(MemberVO.class, member, fields, requiredFields, useImages);
}
/**
* Converts a member to VO
*/
public MemberVO toVO(final Member member, final List<MemberCustomField> fields, final boolean useImages) {
Collection<MemberCustomField> requiredFields = null;
Channel channel = WebServiceContext.getChannel();
if (channel != null && !CollectionUtils.isEmpty(fields)) {
// Ensure that any custom fields used as principal for the current channel are returned in the MemberVO
Collection<ChannelPrincipal> principals = channel.getPrincipals();
for (ChannelPrincipal principal : principals) {
MemberCustomField customField = principal.getCustomField();
if (customField != null && fields.contains(customField)) {
if (requiredFields == null) {
requiredFields = new HashSet<MemberCustomField>();
}
requiredFields.add(customField);
}
}
}
return toVO(member, fields, requiredFields, useImages);
}
private <VO extends MemberVO> VO toVO(final Class<VO> voType, final Member member, final Collection<MemberCustomField> fields, final Collection<MemberCustomField> requiredFields, final boolean useImages) {
if (member == null) {
return null;
}
VO vo;
try {
vo = voType.newInstance();
} catch (Exception e) {
throw new IllegalStateException(e);
}
vo.setId(member.getId());
vo.setName(member.getName());
vo.setUsername(member.getUsername());
vo.setEmail(member.getEmail());
vo.setGroupId(member.getGroup().getId());
if (fields != null) {
final Collection<MemberCustomFieldValue> customValues = ((Member) elementServiceLocal.load(member.getId(), Member.Relationships.CUSTOM_VALUES)).getCustomValues();
vo.setFields(fieldHelper.toList(fields, requiredFields, customValues));
} else {
final List<FieldValueVO> empty = Collections.emptyList();
vo.setFields(empty);
}
if (useImages) {
vo.setImages(imageHelper.toVOs(member.getImages()));
}
return vo;
}
}