nl.strohalm.cyclos.controls.members.messages.SendMessageAction.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.controls.members.messages.SendMessageAction.java

Source

/*
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.controls.members.messages;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import nl.strohalm.cyclos.access.AdminMemberPermission;
import nl.strohalm.cyclos.access.BrokerPermission;
import nl.strohalm.cyclos.access.MemberPermission;
import nl.strohalm.cyclos.access.OperatorPermission;
import nl.strohalm.cyclos.annotations.Inject;
import nl.strohalm.cyclos.controls.ActionContext;
import nl.strohalm.cyclos.controls.BaseFormAction;
import nl.strohalm.cyclos.entities.groups.Group;
import nl.strohalm.cyclos.entities.groups.GroupQuery;
import nl.strohalm.cyclos.entities.groups.MemberGroup;
import nl.strohalm.cyclos.entities.members.Element;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.entities.members.messages.Message;
import nl.strohalm.cyclos.entities.members.messages.Message.Type;
import nl.strohalm.cyclos.entities.members.messages.MessageCategory;
import nl.strohalm.cyclos.entities.members.messages.MessageCategoryQuery;
import nl.strohalm.cyclos.entities.members.preferences.NotificationPreference;
import nl.strohalm.cyclos.entities.settings.LocalSettings;
import nl.strohalm.cyclos.exceptions.PermissionDeniedException;
import nl.strohalm.cyclos.services.elements.MessageCategoryService;
import nl.strohalm.cyclos.services.elements.MessageService;
import nl.strohalm.cyclos.services.elements.SendDirectMessageToMemberDTO;
import nl.strohalm.cyclos.services.elements.SendMessageDTO;
import nl.strohalm.cyclos.services.elements.SendMessageFromBrokerToMembersDTO;
import nl.strohalm.cyclos.services.elements.SendMessageToAdminDTO;
import nl.strohalm.cyclos.services.elements.SendMessageToGroupDTO;
import nl.strohalm.cyclos.services.elements.exceptions.MemberWontReceiveNotificationException;
import nl.strohalm.cyclos.services.preferences.PreferenceService;
import nl.strohalm.cyclos.utils.ActionHelper;
import nl.strohalm.cyclos.utils.TextFormat;
import nl.strohalm.cyclos.utils.binding.BeanBinder;
import nl.strohalm.cyclos.utils.binding.DataBinder;
import nl.strohalm.cyclos.utils.binding.PropertyBinder;
import nl.strohalm.cyclos.utils.binding.SimpleCollectionBinder;
import nl.strohalm.cyclos.utils.conversion.CoercionHelper;
import nl.strohalm.cyclos.utils.conversion.HtmlConverter;
import nl.strohalm.cyclos.utils.conversion.StringTrimmerConverter;
import nl.strohalm.cyclos.utils.transaction.CurrentTransactionData;
import nl.strohalm.cyclos.utils.validation.ValidationException;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.struts.action.ActionForward;

/**
 * Action used to send a message
 * @author luis
 */
public class SendMessageAction extends BaseFormAction {

    /**
     * An enum indicating where to send a message
     * @author luis
     */
    public static enum SendTo {
        MEMBER, ADMIN, GROUP, BROKERED_MEMBERS
    }

    private static final int WRAP_SIZE = 50;

    private MessageService messageService;
    private MessageCategoryService messageCategoryService;
    private PreferenceService preferenceService;
    private Map<Class<? extends SendMessageDTO>, DataBinder<? extends SendMessageDTO>> dataBindersByType;

    @Inject
    public void setMessageCategoryService(final MessageCategoryService messageCategoryService) {
        this.messageCategoryService = messageCategoryService;
    }

    @Inject
    public void setMessageService(final MessageService messageService) {
        this.messageService = messageService;
    }

    @Inject
    public void setPreferenceService(final PreferenceService preferenceService) {
        this.preferenceService = preferenceService;
    }

    @Override
    protected ActionForward handleSubmit(final ActionContext context) throws Exception {
        final SendMessageForm form = context.getForm();
        final long toMemberId = form.getToMemberId();

        // Send the message
        final SendMessageDTO dto = resolveDTO(context);

        // Call the correct service method
        try {
            String key = "message.sent";

            messageService.send(dto);
            if (dto instanceof SendDirectMessageToMemberDTO) {
                final SendDirectMessageToMemberDTO sendDirectMessageToMemberDTO = (SendDirectMessageToMemberDTO) dto;
                Type type = null;

                if (context.isAdmin()) {
                    type = Message.Type.FROM_ADMIN_TO_MEMBER;
                } else {
                    type = Message.Type.FROM_MEMBER;
                }

                if (CurrentTransactionData.hasMailError()) {
                    final Member member = sendDirectMessageToMemberDTO.getToMember();
                    final NotificationPreference np = preferenceService.load(member, type);
                    if (np.isMessage()) {
                        key = "message.warning.messageNotReceivedByEmail";
                    } else {
                        return context.sendError("message.error.emailNotSent");
                    }
                }
            }
            context.sendMessage(key);
        } catch (final MemberWontReceiveNotificationException e) {
            return context.sendError("message.error.memberWontReceiveNotification");
        }

        // Go back to the correct location
        if (dto.getInReplyTo() == null && toMemberId > 0L) {
            return ActionHelper.redirectWithParam(context.getRequest(), context.findForward("backToProfile"),
                    "memberId", toMemberId);
        }

        return context.findForward("backToList");
    }

    @Override
    protected void prepareForm(final ActionContext context) throws Exception {
        final SendMessageForm form = context.getForm();
        final HttpServletRequest request = context.getRequest();
        final Member toMember = resolveToMember(context);
        final Message inReplyTo = resolveInReplyTo(context);

        if (toMember == null) {
            final List<SendTo> sendTo = new ArrayList<SendTo>();
            if (context.isAdmin()) {
                // An admin may send to a group, so, we must get the groups
                if (inReplyTo == null) {
                    final GroupQuery gq = new GroupQuery();
                    gq.setNatures(Group.Nature.MEMBER, Group.Nature.BROKER);
                    gq.setStatus(Group.Status.NORMAL);
                    request.setAttribute("groups", groupService.search(gq));
                    if (permissionService.hasPermission(AdminMemberPermission.MESSAGES_SEND_TO_MEMBER)) {
                        sendTo.add(SendTo.MEMBER);
                    }
                    if (permissionService.hasPermission(AdminMemberPermission.MESSAGES_SEND_TO_GROUP)) {
                        sendTo.add(SendTo.GROUP);
                    }
                }
            } else {
                if (form.isToBrokeredMembers()) {
                    if (context.isBroker()
                            && permissionService.hasPermission(BrokerPermission.MESSAGES_SEND_TO_MEMBERS)) {
                        sendTo.add(SendTo.BROKERED_MEMBERS);
                        request.setAttribute("toBrokeredMembers", SendTo.BROKERED_MEMBERS);
                    }
                } else if (inReplyTo == null) {
                    if (context.isMember()
                            && permissionService.hasPermission(MemberPermission.MESSAGES_SEND_TO_MEMBER)
                            || context.isOperator() && permissionService
                                    .hasPermission(OperatorPermission.MESSAGES_SEND_TO_MEMBER)) {
                        sendTo.add(SendTo.MEMBER);
                    }
                    if (context.isBroker()
                            && permissionService.hasPermission(BrokerPermission.MESSAGES_SEND_TO_MEMBERS)) {
                        sendTo.add(SendTo.BROKERED_MEMBERS);
                    }
                    // A member may send to admin, so we must get the categories
                    final MessageCategoryQuery query = new MessageCategoryQuery();
                    query.setFromElement((Member) context.getAccountOwner());
                    final List<MessageCategory> categories = messageCategoryService.search(query);
                    request.setAttribute("categories", categories);
                    if (CollectionUtils.isNotEmpty(categories) && (context.isMember()
                            && permissionService.hasPermission(MemberPermission.MESSAGES_SEND_TO_ADMINISTRATION)
                            || context.isOperator() && permissionService
                                    .hasPermission(OperatorPermission.MESSAGES_SEND_TO_ADMINISTRATION))) {
                        sendTo.add(SendTo.ADMIN);
                    }
                }
            }
            if (inReplyTo == null && CollectionUtils.isEmpty(sendTo)) {
                throw new PermissionDeniedException();
            }
            request.setAttribute("sendTo", sendTo);
        } else {
            final MessageCategoryQuery query = new MessageCategoryQuery();
            query.setFromElement(
                    (Element) (context.isOperator() ? context.getAccountOwner() : context.getElement()));
            query.setToElement(toMember);
            request.setAttribute("categories", messageCategoryService.search(query));
        }

        // Message reply
        final LocalSettings localSettings = settingsService.getLocalSettings();
        TextFormat messageFormat = localSettings.getMessageFormat();
        if (inReplyTo != null) {
            form.setMessage("subject", context.message("message.reply.subject", inReplyTo.getSubject()));
            String body;
            if (inReplyTo.isHtml()) {
                body = "<br><br><div style='padding-left:40px;border-left:1px solid black'>" + inReplyTo.getBody()
                        + "</div>";
                messageFormat = TextFormat.RICH;
            } else {
                body = " \n\n> "
                        + StringUtils.replace(WordUtils.wrap(inReplyTo.getBody(), WRAP_SIZE), "\n", "\n> ");
                messageFormat = TextFormat.PLAIN;
            }
            request.setAttribute("body", body);
            form.setMessage("html", inReplyTo.isHtml());
            if (inReplyTo.getCategory() != null) {
                form.setMessage("category", inReplyTo.getCategory().getId());
                if (inReplyTo.getToMember() != null) {
                    // Reply to a member
                    request.setAttribute("categoryName", inReplyTo.getCategory().getName());
                    request.setAttribute("categoryEditable", false);
                } else {
                    // Reply to administration
                    final MessageCategoryQuery query = new MessageCategoryQuery();
                    query.setFromElement(
                            (Element) (context.isOperator() ? context.getAccountOwner() : context.getElement()));
                    request.setAttribute("categories", messageCategoryService.search(query));
                    request.setAttribute("categoryId", inReplyTo.getCategory().getId());
                }
            }
        }
        form.setMessage("html", messageFormat == TextFormat.RICH);
        request.setAttribute("inReplyTo", inReplyTo);
        request.setAttribute("toMember", toMember);
        request.setAttribute("messageFormat", messageFormat);
    }

    @Override
    protected void validateForm(final ActionContext context) {
        final SendMessageDTO dto = resolveDTO(context);
        messageService.validate(dto);
    }

    private <T extends SendMessageDTO> BeanBinder<T> basicDataBinderFor(final Class<T> type) {
        final BeanBinder<T> binder = BeanBinder.instance(type);
        // The body is not read here, as it can be either plain text or html
        binder.registerBinder("category", PropertyBinder.instance(MessageCategory.class, "category"));
        binder.registerBinder("subject", PropertyBinder.instance(String.class, "subject"));
        binder.registerBinder("inReplyTo", PropertyBinder.instance(Message.class, "inReplyTo"));
        binder.registerBinder("html", PropertyBinder.instance(Boolean.TYPE, "html"));
        return binder;
    }

    @SuppressWarnings("unchecked")
    private <T extends SendMessageDTO> DataBinder<T> getDataBinderFor(final Class<T> type) {
        if (dataBindersByType == null) {
            dataBindersByType = new HashMap<Class<? extends SendMessageDTO>, DataBinder<? extends SendMessageDTO>>();
            final BeanBinder<SendDirectMessageToMemberDTO> toMemberBinder = basicDataBinderFor(
                    SendDirectMessageToMemberDTO.class);
            toMemberBinder.registerBinder("toMember", PropertyBinder.instance(Member.class, "toMember"));
            dataBindersByType.put(SendDirectMessageToMemberDTO.class, toMemberBinder);

            final BeanBinder<SendMessageToAdminDTO> toAdminBinder = basicDataBinderFor(SendMessageToAdminDTO.class);
            dataBindersByType.put(SendMessageToAdminDTO.class, toAdminBinder);

            final BeanBinder<SendMessageFromBrokerToMembersDTO> toBrokeredBinder = basicDataBinderFor(
                    SendMessageFromBrokerToMembersDTO.class);
            dataBindersByType.put(SendMessageFromBrokerToMembersDTO.class, toBrokeredBinder);

            final BeanBinder<SendMessageToGroupDTO> toGroupBinder = basicDataBinderFor(SendMessageToGroupDTO.class);
            toGroupBinder.registerBinder("toGroups",
                    SimpleCollectionBinder.instance(MemberGroup.class, "toGroups"));
            dataBindersByType.put(SendMessageToGroupDTO.class, toGroupBinder);
        }
        return (DataBinder<T>) dataBindersByType.get(type);
    }

    /**
     * Resolve a send message dto
     */
    private SendMessageDTO resolveDTO(final ActionContext context) {
        final SendMessageForm form = context.getForm();
        Class<? extends SendMessageDTO> dtoClass = null;
        final SendTo sendTo = CoercionHelper.coerce(SendTo.class, form.getSendTo());
        if (sendTo == null) {
            throw new ValidationException();
        }
        // Test and validate who to send the message
        switch (sendTo) {
        case MEMBER:
            dtoClass = SendDirectMessageToMemberDTO.class;
            break;
        case GROUP:
            if (!context.isAdmin()) {
                throw new ValidationException();
            }
            dtoClass = SendMessageToGroupDTO.class;
            break;
        case BROKERED_MEMBERS:
            if (!context.isBroker()) {
                throw new ValidationException();
            }
            dtoClass = SendMessageFromBrokerToMembersDTO.class;
            break;
        case ADMIN:
            if (!(context.isMember() || context.isOperator())) {
                throw new ValidationException();
            }
            dtoClass = SendMessageToAdminDTO.class;
            break;
        default:
            throw new ValidationException();
        }

        final SendMessageDTO dto = getDataBinderFor(dtoClass).readFromString(form.getMessage());
        if (dto.isHtml()) {
            dto.setBody(HtmlConverter.instance().valueOf("" + form.getMessage("body")));
        } else {
            dto.setBody(StringTrimmerConverter.instance().valueOf("" + form.getMessage("body")));
        }
        return dto;
    }

    private Message resolveInReplyTo(final ActionContext context) {
        final SendMessageForm form = context.getForm();
        final long inReplyToId = form.getInReplyTo();
        if (inReplyToId <= 0L) {
            return null;
        }
        final Message inReplyTo = messageService.load(inReplyToId, Message.Relationships.TO_MEMBER);
        if ((context.isAdmin() && inReplyTo.getToMember() != null)
                || (context.isMember() && !context.getAccountOwner().equals(inReplyTo.getToMember()))) {
            throw new PermissionDeniedException();
        }
        return inReplyTo;
    }

    /**
     * Resolve the member to send to, if any
     */
    private Member resolveToMember(final ActionContext context) {
        final SendMessageForm form = context.getForm();
        final long toMemberId = form.getToMemberId();
        Member toMember = null;

        // Load the member to send to, if any
        if (toMemberId > 0L) {
            final Element loggedElement = (Element) (context.isOperator() ? context.getAccountOwner()
                    : context.getElement());
            // Cannot send to self
            if (toMemberId == loggedElement.getId()) {
                throw new ValidationException();
            }
            // Ensure a member
            final Element element = elementService.load(toMemberId, Element.Relationships.USER);
            if (!(element instanceof Member)) {
                throw new ValidationException();
            }
            toMember = (Member) element;
        }

        return toMember;
    }

}