nl.strohalm.cyclos.controls.customization.fields.EditCustomFieldAction.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.controls.customization.fields.EditCustomFieldAction.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.customization.fields;

import java.util.Collection;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import nl.strohalm.cyclos.access.AdminSystemPermission;
import nl.strohalm.cyclos.access.MemberPermission;
import nl.strohalm.cyclos.annotations.Inject;
import nl.strohalm.cyclos.controls.ActionContext;
import nl.strohalm.cyclos.controls.BaseFormAction;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferType;
import nl.strohalm.cyclos.entities.customization.fields.AdCustomField;
import nl.strohalm.cyclos.entities.customization.fields.AdminCustomField;
import nl.strohalm.cyclos.entities.customization.fields.CustomField;
import nl.strohalm.cyclos.entities.customization.fields.CustomFieldPossibleValue;
import nl.strohalm.cyclos.entities.customization.fields.LoanGroupCustomField;
import nl.strohalm.cyclos.entities.customization.fields.MemberCustomField;
import nl.strohalm.cyclos.entities.customization.fields.MemberRecordCustomField;
import nl.strohalm.cyclos.entities.customization.fields.OperatorCustomField;
import nl.strohalm.cyclos.entities.customization.fields.PaymentCustomField;
import nl.strohalm.cyclos.entities.customization.fields.Validation;
import nl.strohalm.cyclos.entities.groups.AdminGroup;
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.Member;
import nl.strohalm.cyclos.entities.members.records.MemberRecordType;
import nl.strohalm.cyclos.services.customization.AdCustomFieldService;
import nl.strohalm.cyclos.services.customization.AdminCustomFieldService;
import nl.strohalm.cyclos.services.customization.BaseCustomFieldService;
import nl.strohalm.cyclos.services.customization.LoanGroupCustomFieldService;
import nl.strohalm.cyclos.services.customization.MemberCustomFieldService;
import nl.strohalm.cyclos.services.customization.MemberRecordCustomFieldService;
import nl.strohalm.cyclos.services.customization.OperatorCustomFieldService;
import nl.strohalm.cyclos.services.customization.PaymentCustomFieldService;
import nl.strohalm.cyclos.services.elements.MemberRecordTypeService;
import nl.strohalm.cyclos.services.transfertypes.TransferTypeService;
import nl.strohalm.cyclos.utils.ActionHelper;
import nl.strohalm.cyclos.utils.RequestHelper;
import nl.strohalm.cyclos.utils.binding.BeanBinder;
import nl.strohalm.cyclos.utils.binding.DataBinder;
import nl.strohalm.cyclos.utils.binding.DataBinderHelper;
import nl.strohalm.cyclos.utils.binding.PropertyBinder;
import nl.strohalm.cyclos.utils.binding.SimpleCollectionBinder;
import nl.strohalm.cyclos.utils.conversion.IdConverter;
import nl.strohalm.cyclos.utils.validation.ValidationException;

import org.apache.commons.collections.CollectionUtils;
import org.apache.struts.action.ActionForward;

/**
 * Action used to edit a custom field
 * @author luis
 */
public class EditCustomFieldAction extends BaseFormAction {

    private static DataBinder<? extends CustomField> getBasicDataBinder(final CustomField.Nature nature) {

        final BeanBinder<Validation> validationBinder = BeanBinder.instance(Validation.class, "validation");
        validationBinder.registerBinder("required", PropertyBinder.instance(Boolean.TYPE, "required"));
        validationBinder.registerBinder("unique", PropertyBinder.instance(Boolean.TYPE, "unique"));
        validationBinder.registerBinder("lengthConstraint",
                DataBinderHelper.rangeConstraintBinder("lengthConstraint"));
        validationBinder.registerBinder("validatorClass", PropertyBinder.instance(String.class, "validatorClass"));

        final BeanBinder<? extends CustomField> binder = BeanBinder.instance(nature.getEntityType());
        binder.registerBinder("id", PropertyBinder.instance(Long.class, "id", IdConverter.instance()));
        binder.registerBinder("internalName", PropertyBinder.instance(String.class, "internalName"));
        binder.registerBinder("name", PropertyBinder.instance(String.class, "name"));
        binder.registerBinder("pattern", PropertyBinder.instance(String.class, "pattern"));
        binder.registerBinder("parent", PropertyBinder.instance(CustomField.class, "parent"));
        binder.registerBinder("description", PropertyBinder.instance(String.class, "description"));
        binder.registerBinder("type", PropertyBinder.instance(CustomField.Type.class, "type"));
        binder.registerBinder("control", PropertyBinder.instance(CustomField.Control.class, "control"));
        binder.registerBinder("size", PropertyBinder.instance(CustomField.Size.class, "size"));
        binder.registerBinder("allSelectedLabel", PropertyBinder.instance(String.class, "allSelectedLabel"));
        binder.registerBinder("validation", validationBinder);
        return binder;
    }

    private AdCustomFieldService adCustomFieldService;
    private AdminCustomFieldService adminCustomFieldService;
    private LoanGroupCustomFieldService loanGroupCustomFieldService;
    private MemberCustomFieldService memberCustomFieldService;
    private MemberRecordCustomFieldService memberRecordCustomFieldService;
    private OperatorCustomFieldService operatorCustomFieldService;
    private PaymentCustomFieldService paymentCustomFieldService;
    private MemberRecordTypeService memberRecordTypeService;
    private TransferTypeService transferTypeService;
    private Map<CustomField.Nature, DataBinder<? extends CustomField>> dataBinders;

    @SuppressWarnings("unchecked")
    public DataBinder<AdCustomField> getAdCustomFieldBinder() {
        final BeanBinder<AdCustomField> adFieldBinder = (BeanBinder<AdCustomField>) getBasicDataBinder(
                CustomField.Nature.AD);
        adFieldBinder.registerBinder("showInSearch", PropertyBinder.instance(Boolean.TYPE, "showInSearch"));
        adFieldBinder.registerBinder("indexed", PropertyBinder.instance(Boolean.TYPE, "indexed"));
        adFieldBinder.registerBinder("visibility",
                PropertyBinder.instance(AdCustomField.Visibility.class, "visibility"));
        return adFieldBinder;
    }

    @SuppressWarnings("unchecked")
    public DataBinder<AdminCustomField> getAdminCustomFieldBinder() {
        final BeanBinder<AdminCustomField> adminFieldBinder = (BeanBinder<AdminCustomField>) getBasicDataBinder(
                CustomField.Nature.ADMIN);
        adminFieldBinder.registerBinder("groups", SimpleCollectionBinder.instance(AdminGroup.class, "groups"));
        return adminFieldBinder;
    }

    public DataBinder<? extends CustomField> getDataBinder(final CustomField.Nature nature) {
        if (dataBinders == null) {
            dataBinders = new EnumMap<CustomField.Nature, DataBinder<? extends CustomField>>(
                    CustomField.Nature.class);
            dataBinders.put(CustomField.Nature.MEMBER, getMemberCustomFieldBinder());
            dataBinders.put(CustomField.Nature.ADMIN, getAdminCustomFieldBinder());
            dataBinders.put(CustomField.Nature.OPERATOR, getOperatorCustomFieldBinder());
            dataBinders.put(CustomField.Nature.AD, getAdCustomFieldBinder());
            dataBinders.put(CustomField.Nature.PAYMENT, getPaymentCustomFieldBinder());
            dataBinders.put(CustomField.Nature.LOAN_GROUP, getLoanGroupCustomFieldBinder());
            dataBinders.put(CustomField.Nature.MEMBER_RECORD, getMemberRecordCustomFieldBinder());
        }
        return dataBinders.get(nature);
    }

    @SuppressWarnings("unchecked")
    public DataBinder<LoanGroupCustomField> getLoanGroupCustomFieldBinder() {
        final BeanBinder<LoanGroupCustomField> loanGroupFieldBinder = (BeanBinder<LoanGroupCustomField>) getBasicDataBinder(
                CustomField.Nature.LOAN_GROUP);
        loanGroupFieldBinder.registerBinder("showInSearch", PropertyBinder.instance(Boolean.TYPE, "showInSearch"));
        return loanGroupFieldBinder;
    }

    @SuppressWarnings("unchecked")
    public DataBinder<MemberCustomField> getMemberCustomFieldBinder() {
        final BeanBinder<MemberCustomField> memberFieldBinder = (BeanBinder<MemberCustomField>) getBasicDataBinder(
                CustomField.Nature.MEMBER);
        memberFieldBinder.registerBinder("visibilityAccess",
                PropertyBinder.instance(MemberCustomField.Access.class, "visibilityAccess"));
        memberFieldBinder.registerBinder("updateAccess",
                PropertyBinder.instance(MemberCustomField.Access.class, "updateAccess"));
        memberFieldBinder.registerBinder("memberSearchAccess",
                PropertyBinder.instance(MemberCustomField.Access.class, "memberSearchAccess"));
        memberFieldBinder.registerBinder("adSearchAccess",
                PropertyBinder.instance(MemberCustomField.Access.class, "adSearchAccess"));
        memberFieldBinder.registerBinder("indexing",
                PropertyBinder.instance(MemberCustomField.Indexing.class, "indexing"));
        memberFieldBinder.registerBinder("loanSearchAccess",
                PropertyBinder.instance(MemberCustomField.Access.class, "loanSearchAccess"));
        memberFieldBinder.registerBinder("memberCanHide", PropertyBinder.instance(Boolean.TYPE, "memberCanHide"));
        memberFieldBinder.registerBinder("showInPrint", PropertyBinder.instance(Boolean.TYPE, "showInPrint"));
        memberFieldBinder.registerBinder("groups", SimpleCollectionBinder.instance(MemberGroup.class, "groups"));
        return memberFieldBinder;
    }

    @SuppressWarnings("unchecked")
    public DataBinder<MemberRecordCustomField> getMemberRecordCustomFieldBinder() {
        final BeanBinder<MemberRecordCustomField> memberRecordFieldBinder = (BeanBinder<MemberRecordCustomField>) getBasicDataBinder(
                CustomField.Nature.MEMBER_RECORD);
        memberRecordFieldBinder.registerBinder("memberRecordType",
                PropertyBinder.instance(MemberRecordType.class, "memberRecordType"));
        memberRecordFieldBinder.registerBinder("showInSearch",
                PropertyBinder.instance(Boolean.TYPE, "showInSearch"));
        memberRecordFieldBinder.registerBinder("showInList", PropertyBinder.instance(Boolean.TYPE, "showInList"));
        memberRecordFieldBinder.registerBinder("brokerAccess",
                PropertyBinder.instance(MemberRecordCustomField.Access.class, "brokerAccess"));
        return memberRecordFieldBinder;
    }

    @SuppressWarnings("unchecked")
    public DataBinder<OperatorCustomField> getOperatorCustomFieldBinder() {
        final BeanBinder<OperatorCustomField> operatorFieldBinder = (BeanBinder<OperatorCustomField>) getBasicDataBinder(
                CustomField.Nature.OPERATOR);
        operatorFieldBinder.registerBinder("member", PropertyBinder.instance(Member.class, "member"));
        operatorFieldBinder.registerBinder("visibility",
                PropertyBinder.instance(OperatorCustomField.Visibility.class, "visibility"));
        return operatorFieldBinder;
    }

    @SuppressWarnings("unchecked")
    public DataBinder<PaymentCustomField> getPaymentCustomFieldBinder() {
        final BeanBinder<PaymentCustomField> paymentFieldBinder = (BeanBinder<PaymentCustomField>) getBasicDataBinder(
                CustomField.Nature.PAYMENT);
        paymentFieldBinder.registerBinder("enabled", PropertyBinder.instance(Boolean.TYPE, "enabled"));
        paymentFieldBinder.registerBinder("transferType",
                PropertyBinder.instance(TransferType.class, "transferType"));
        paymentFieldBinder.registerBinder("searchAccess",
                PropertyBinder.instance(PaymentCustomField.Access.class, "searchAccess"));
        paymentFieldBinder.registerBinder("listAccess",
                PropertyBinder.instance(PaymentCustomField.Access.class, "listAccess"));
        return paymentFieldBinder;
    }

    @Inject
    public void setAdCustomFieldService(final AdCustomFieldService adCustomFieldService) {
        this.adCustomFieldService = adCustomFieldService;
    }

    @Inject
    public void setAdminCustomFieldService(final AdminCustomFieldService adminCustomFieldService) {
        this.adminCustomFieldService = adminCustomFieldService;
    }

    @Inject
    public void setLoanGroupCustomFieldService(final LoanGroupCustomFieldService loanGroupCustomFieldService) {
        this.loanGroupCustomFieldService = loanGroupCustomFieldService;
    }

    @Inject
    public void setMemberCustomFieldService(final MemberCustomFieldService memberCustomFieldService) {
        this.memberCustomFieldService = memberCustomFieldService;
    }

    @Inject
    public void setMemberRecordCustomFieldService(
            final MemberRecordCustomFieldService memberRecordCustomFieldService) {
        this.memberRecordCustomFieldService = memberRecordCustomFieldService;
    }

    @Inject
    public void setMemberRecordTypeService(final MemberRecordTypeService memberRecordTypeService) {
        this.memberRecordTypeService = memberRecordTypeService;
    }

    @Inject
    public void setOperatorCustomFieldService(final OperatorCustomFieldService operatorCustomFieldService) {
        this.operatorCustomFieldService = operatorCustomFieldService;
    }

    @Inject
    public void setPaymentCustomFieldService(final PaymentCustomFieldService paymentCustomFieldService) {
        this.paymentCustomFieldService = paymentCustomFieldService;
    }

    @Inject
    public void setTransferTypeService(final TransferTypeService transferTypeService) {
        this.transferTypeService = transferTypeService;
    }

    @Override
    protected ActionForward handleSubmit(final ActionContext context) throws Exception {
        final EditCustomFieldForm form = context.getForm();
        final CustomField.Nature nature = getNature(form);
        CustomField field = getDataBinder(nature).readFromString(form.getField());
        final boolean isInsert = field.getId() == null;
        if (isInsert && field instanceof OperatorCustomField) {
            ((OperatorCustomField) field).setMember((Member) context.getElement());
        }
        field = resolveService(nature).save(field);

        // Forward with correct parameters
        final Map<String, Object> params = new HashMap<String, Object>();
        params.put("fieldId", field.getId());
        params.put("nature", field.getNature().name());
        switch (nature) {
        case MEMBER_RECORD:
            params.put("memberRecordTypeId", form.getField("memberRecordType"));
            break;
        case PAYMENT:
            final PaymentCustomField paymentField = (PaymentCustomField) field;
            params.put("accountTypeId", paymentField.getTransferType().getFrom().getId());
            params.put("transferTypeId", paymentField.getTransferType().getId());
            break;
        }
        context.sendMessage(isInsert ? "customField.inserted" : "customField.modified");
        return ActionHelper.redirectWithParams(context.getRequest(), context.getSuccessForward(), params);
    }

    @Override
    protected void prepareForm(final ActionContext context) throws Exception {
        final HttpServletRequest request = context.getRequest();
        final EditCustomFieldForm form = context.getForm();
        final long id = form.getFieldId();
        final CustomField.Nature nature = getNature(form);
        CustomField field;
        final BaseCustomFieldService<CustomField> service = resolveService(nature);
        if (id <= 0L) {
            field = nature.getEntityType().newInstance();
            switch (nature) {
            case OPERATOR:
                ((OperatorCustomField) field).setMember((Member) context.getElement());
                break;
            case MEMBER_RECORD:
                final MemberRecordType memberRecordType = memberRecordTypeService
                        .load(form.getMemberRecordTypeId());
                ((MemberRecordCustomField) field).setMemberRecordType(memberRecordType);
                break;
            case PAYMENT:
                final TransferType transferType = transferTypeService.load(form.getTransferTypeId());
                ((PaymentCustomField) field).setTransferType(transferType);
                break;
            }
            // Retrieve the possible parent fields
            final List<? extends CustomField> possibleParentFields = service.listPossibleParentFields(field);
            request.setAttribute("possibleParentFields", possibleParentFields);
        } else {
            field = service.load(id);

            // Get the possible values according to the selected parent value, if any
            Collection<CustomFieldPossibleValue> possibleValues;
            final CustomField parent = field.getParent();
            if (parent == null) {
                // No parent - use all
                possibleValues = field.getPossibleValues(false);
            } else {
                final long parentValueId = form.getParentValueId();
                CustomFieldPossibleValue parentValue = null;
                if (parentValueId > 0L) {
                    // There's a parent value - load it
                    parentValue = service.loadPossibleValue(parentValueId);
                } else {
                    // No parent value selected - check whether the parent has at least one and select it
                    if (parent != null && CollectionUtils.isNotEmpty(parent.getPossibleValues(false))) {
                        parentValue = parent.getPossibleValues(false).iterator().next();
                        form.setParentValueId(parentValue.getId());
                    }
                }
                possibleValues = field.getPossibleValuesByParent(parentValue, false);
            }
            request.setAttribute("possibleValues", possibleValues);
        }

        getDataBinder(nature).writeAsString(form.getField(), field);
        request.setAttribute("field", field);
        request.setAttribute("nature", nature.name());
        RequestHelper.storeEnum(request, CustomField.Control.class, "controls");
        RequestHelper.storeEnum(request, CustomField.Type.class, "types");
        RequestHelper.storeEnum(request, CustomField.Size.class, "sizes");

        // Check the whether the field can be managed
        boolean canManage = false;
        switch (nature) {
        case OPERATOR:
            canManage = permissionService.hasPermission(MemberPermission.OPERATORS_MANAGE);
            break;
        case MEMBER_RECORD:
            canManage = permissionService.hasPermission(AdminSystemPermission.MEMBER_RECORD_TYPES_MANAGE);
            break;
        case PAYMENT:
            final PaymentCustomField paymentField = (PaymentCustomField) field;
            final TransferType backToTransferType = transferTypeService
                    .load(paymentField.getTransferType().getId());
            if (paymentField.getTransferType().equals(backToTransferType)) {
                // When should back to another TT, the form is not editable
                canManage = permissionService.hasPermission(AdminSystemPermission.ACCOUNTS_MANAGE);
            }
            request.setAttribute("backToTransferType", backToTransferType);
            break;
        default:
            canManage = permissionService.hasPermission(AdminSystemPermission.CUSTOM_FIELDS_MANAGE);
            break;
        }
        request.setAttribute("canManage", canManage);

        // Store specific nature values
        final GroupQuery groupQuery = new GroupQuery();
        groupQuery.setStatus(Group.Status.NORMAL);
        switch (nature) {
        case MEMBER:
            // View may be any access level but WEB_SERVICE
            request.setAttribute("accessForView",
                    EnumSet.complementOf(EnumSet.of(MemberCustomField.Access.WEB_SERVICE)));
            // Edit access level can be any but OTHER and WEB_SERVICE
            request.setAttribute("accessForEdit", EnumSet.complementOf(
                    EnumSet.of(MemberCustomField.Access.OTHER, MemberCustomField.Access.WEB_SERVICE)));
            // The member search and ads search includes WEB_SERVICE
            request.setAttribute("memberAndAdsAccess",
                    EnumSet.of(MemberCustomField.Access.NONE, MemberCustomField.Access.WEB_SERVICE,
                            MemberCustomField.Access.ADMIN, MemberCustomField.Access.BROKER,
                            MemberCustomField.Access.MEMBER));
            // The others (searches, etc.) are only NONE, ADMIN, BROKER or MEMBER
            request.setAttribute("access", EnumSet.of(MemberCustomField.Access.NONE, MemberCustomField.Access.ADMIN,
                    MemberCustomField.Access.BROKER, MemberCustomField.Access.MEMBER));
            groupQuery.setNatures(Group.Nature.MEMBER, Group.Nature.BROKER);
            request.setAttribute("groups", groupService.search(groupQuery));
            RequestHelper.storeEnum(request, MemberCustomField.Indexing.class, "indexings");
            break;
        case ADMIN:
            groupQuery.setNatures(Group.Nature.ADMIN);
            request.setAttribute("groups", groupService.search(groupQuery));
            break;
        case OPERATOR:
            RequestHelper.storeEnum(request, OperatorCustomField.Visibility.class, "visibilities");
            break;
        case AD:
            RequestHelper.storeEnum(request, AdCustomField.Visibility.class, "visibilities");
            break;
        case MEMBER_RECORD:
            final MemberRecordCustomField memberRecordCustomField = (MemberRecordCustomField) field;
            request.setAttribute("memberRecordType", memberRecordCustomField.getMemberRecordType());
            RequestHelper.storeEnum(request, MemberRecordCustomField.Access.class, "accesses");
            break;
        case PAYMENT:
            final PaymentCustomField paymentCustomField = (PaymentCustomField) field;
            final TransferType transferType = paymentCustomField.getTransferType();
            request.setAttribute("transferType", transferType);
            final Set<PaymentCustomField.Access> accesses = EnumSet.allOf(PaymentCustomField.Access.class);
            if (paymentCustomField.getTransferType().getFixedDestinationMember() == null) {
                accesses.remove(PaymentCustomField.Access.DESTINATION_MEMBER);
            }
            request.setAttribute("accesses", accesses);
            break;
        }
    }

    @Override
    protected void validateForm(final ActionContext context) {
        final EditCustomFieldForm form = context.getForm();
        final CustomField.Nature nature = getNature(form);
        final CustomField field = getDataBinder(nature).readFromString(form.getField());
        resolveService(nature).validate(field);
    }

    private CustomField.Nature getNature(final EditCustomFieldForm form) {
        CustomField.Nature nature;
        try {
            nature = CustomField.Nature.valueOf(form.getNature());
        } catch (final Exception e) {
            throw new ValidationException();
        }
        return nature;
    }

    @SuppressWarnings("unchecked")
    private <CF extends CustomField> BaseCustomFieldService<CF> resolveService(final CustomField.Nature nature) {
        switch (nature) {
        case AD:
            return (BaseCustomFieldService<CF>) adCustomFieldService;
        case ADMIN:
            return (BaseCustomFieldService<CF>) adminCustomFieldService;
        case LOAN_GROUP:
            return (BaseCustomFieldService<CF>) loanGroupCustomFieldService;
        case MEMBER:
            return (BaseCustomFieldService<CF>) memberCustomFieldService;
        case MEMBER_RECORD:
            return (BaseCustomFieldService<CF>) memberRecordCustomFieldService;
        case OPERATOR:
            return (BaseCustomFieldService<CF>) operatorCustomFieldService;
        case PAYMENT:
            return (BaseCustomFieldService<CF>) paymentCustomFieldService;
        }
        return null;
    }

}