org.kuali.coeus.sys.framework.model.KcTransactionalDocumentFormBase.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.coeus.sys.framework.model.KcTransactionalDocumentFormBase.java

Source

/*
 * Kuali Coeus, a comprehensive research administration system for higher education.
 * 
 * Copyright 2005-2015 Kuali, Inc.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.coeus.sys.framework.model;

import org.apache.commons.lang3.StringUtils;
import org.kuali.coeus.common.framework.person.attr.PersonEditableField;
import org.kuali.coeus.sys.framework.service.KcServiceLocator;
import org.kuali.coeus.sys.framework.validation.SoftError;
import org.kuali.kra.authorization.KraAuthorizationConstants;
import org.kuali.kra.infrastructure.KeyConstants;
import org.kuali.kra.krms.KcKrmsConstants;
import org.kuali.coeus.common.questionnaire.framework.core.MultiQuestionableFormInterface;
import org.kuali.coeus.common.questionnaire.framework.core.QuestionableFormInterface;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.kim.api.KimConstants;
import org.kuali.rice.kns.document.authorization.DocumentAuthorizerBase;
import org.kuali.rice.kns.web.struts.form.KualiTransactionalDocumentFormBase;
import org.kuali.rice.kns.web.ui.ExtraButton;
import org.kuali.rice.krad.document.Document;
import org.kuali.rice.krad.document.authorization.PessimisticLock;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * This class isbase class for KC Transactional Documents ...
 */
public abstract class KcTransactionalDocumentFormBase extends KualiTransactionalDocumentFormBase {

    private static final long serialVersionUID = 1161569719154606103L;

    protected String actionName;
    protected String navigateTo;

    private boolean viewOnly;
    private boolean popupViewOnly;

    private boolean medusaOpenedDoc;
    private Map<String, Boolean> personEditableFields;

    private List<String> unitRulesMessages = new ArrayList<String>();

    public String getActionName() {
        return actionName;
    }

    public void setActionName(String actionName) {
        this.actionName = actionName;
    }

    public String getNavigateTo() {
        return navigateTo;
    }

    public void setNavigateTo(String navigateTo) {
        this.navigateTo = navigateTo;
    }

    @Override
    public void populate(HttpServletRequest request) {
        super.populate(request);

        // Hack to get panels with add/delete items that are editable after add (Protocol Participants, Special Review) to work correctly with validation.  
        // In this scenario, the user adds a couple of correctly formatted items but then changes one of the fields to an incorrect format and saves.  This will
        // cause validation errors, but if the user now tries to delete the errant entry, validation will fail because the validator in the Kuali Request 
        // ProcessDefinitionDefinitionor still detects the error in the message map.  We don't want validation to run for a delete method, so we need to clear the current error 
        // messages, preventing the validator from running and allowing the delete to go through.
        //
        // This is detected by the existence of "validate0" on the methodToCall property (similar to finding the line number of a delete).
        String methodToCallAttribute = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
        if (StringUtils.contains(methodToCallAttribute, "validate")) {
            String validateParameter = StringUtils.substringBetween(methodToCallAttribute, ".validate", ".");
            if (StringUtils.equals("0", validateParameter)) {
                GlobalVariables.getMessageMap().clearErrorMessages();
            }
        }
    }

    /**
     * Consume SoftErrors (if any) and return the collection
     * @return
     */
    @SuppressWarnings("unchecked")
    public Map<String, Collection<SoftError>> getSoftErrors() {
        return (Map<String, Collection<SoftError>>) GlobalVariables.getUserSession()
                .retrieveObject(KeyConstants.SOFT_ERRORS_KEY);
    }

    public void setupLockRegions() {
        String lockRegion = getLockRegion();
        GlobalVariables.getUserSession().addObject(KraAuthorizationConstants.ACTIVE_LOCK_REGION,
                (Object) lockRegion);
        boolean activeLockRegionChangedInd = hasActiveLockRegionChanged(getDocument(), lockRegion);
        GlobalVariables.getUserSession().addObject(KraAuthorizationConstants.LOCK_REGION_CHANGE_IND,
                activeLockRegionChangedInd);
    }

    private boolean hasActiveLockRegionChanged(Document document, String activeLockRegion) {
        if (StringUtils.isNotEmpty(activeLockRegion)) {
            for (PessimisticLock lock : document.getPessimisticLocks()) {
                if (lock.getLockDescriptor() != null && !lock.getLockDescriptor().contains(activeLockRegion)
                        && lock.getOwnedByUser().getPrincipalId()
                                .equals(GlobalVariables.getUserSession().getPrincipalId())) {
                    return true;
                }
            }
        } else if (document.getPessimisticLocks().size() > 0) {
            return true;
        }

        return false;
    }

    protected boolean hasModifyProposalPermission(Map editMode) {
        if (editMode != null && editMode.containsKey(KraAuthorizationConstants.ProposalEditMode.MODIFY_PROPOSAL)) {
            String modifyProposalPermission = (String) editMode
                    .get(KraAuthorizationConstants.ProposalEditMode.MODIFY_PROPOSAL);
            return ((ObjectUtils.isNotNull(modifyProposalPermission))
                    && (DocumentAuthorizerBase.EDIT_MODE_DEFAULT_TRUE_VALUE.equals(modifyProposalPermission)));
        }

        return false;
    }

    protected boolean hasModifyBudgetPermission(Map editMode) {
        String modifyBudgetPermission = "";
        boolean hasModifyBudgetPermission = false;

        if (editMode != null && editMode.containsKey(KraAuthorizationConstants.BudgetEditMode.MODIFY_BUDGET)) {
            modifyBudgetPermission = (String) editMode.get(KraAuthorizationConstants.BudgetEditMode.MODIFY_BUDGET);
            hasModifyBudgetPermission = ((ObjectUtils.isNotNull(modifyBudgetPermission))
                    && (DocumentAuthorizerBase.EDIT_MODE_DEFAULT_TRUE_VALUE.equals(modifyBudgetPermission)));
        }

        //Included the new addBudget permission as well - For the New AuthZ Framework
        if (!hasModifyBudgetPermission && editMode != null && editMode.containsKey("addBudget")) {
            modifyBudgetPermission = (String) editMode.get("addBudget");
            hasModifyBudgetPermission = ((ObjectUtils.isNotNull(modifyBudgetPermission))
                    && (DocumentAuthorizerBase.EDIT_MODE_DEFAULT_TRUE_VALUE.equals(modifyBudgetPermission)));
        }

        return hasModifyBudgetPermission;
    }

    protected boolean hasModifyCompletedBudgetPermission(Map editMode) {
        if (editMode != null && editMode.containsKey("modifyCompletedBudgets")) {
            String modifyBudgetPermission = (String) editMode.get("modifyCompletedBudgets");
            editMode.remove("modifyCompletedBudgets");
            return ((ObjectUtils.isNotNull(modifyBudgetPermission))
                    && (DocumentAuthorizerBase.EDIT_MODE_DEFAULT_TRUE_VALUE.equals(modifyBudgetPermission)));
        }

        return false;
    }

    protected boolean hasModifyNarrativesPermission(Map editMode) {
        if (editMode != null && editMode.containsKey(KraAuthorizationConstants.ProposalEditMode.ADD_NARRATIVES)) {
            String modifyNarrativesPermission = (String) editMode
                    .get(KraAuthorizationConstants.ProposalEditMode.ADD_NARRATIVES);
            return ((ObjectUtils.isNotNull(modifyNarrativesPermission))
                    && (DocumentAuthorizerBase.EDIT_MODE_DEFAULT_TRUE_VALUE.equals(modifyNarrativesPermission)));
        }

        return false;
    }

    /**
     * Get the Header Dispatch.  This determines the action that will occur
     * when the user switches tabs for a document.  If the user can modify
     * the document, the document is automatically saved.  If not (view-only),
     * then a reload will be executed instead.
     * @return the Header Dispatch action
     */
    public String getHeaderDispatch() {
        return this.getDocumentActions().containsKey(KRADConstants.KUALI_ACTION_CAN_SAVE) ? "save" : "reload";
    }

    protected abstract String getLockRegion();

    protected abstract void setSaveDocumentControl(Map editMode);

    public final boolean isViewOnly() {
        return viewOnly;
    }

    public final void setViewOnly(boolean viewOnly) {
        this.viewOnly = viewOnly;
    }

    public final boolean isPopupViewOnly() {
        return popupViewOnly;
    }

    public final void setPopupViewOnly(boolean popupViewOnly) {
        this.popupViewOnly = popupViewOnly;
    }

    @Override
    public void setDocument(Document document) {
        super.setDocument(document);
        ((KcTransactionalDocumentBase) document).setViewOnly(isViewOnly());
    }

    @Override
    protected abstract String getDefaultDocumentTypeName();

    public boolean isMedusaOpenedDoc() {
        return medusaOpenedDoc;
    }

    public void setMedusaOpenedDoc(boolean medusaOpenedDoc) {
        this.medusaOpenedDoc = medusaOpenedDoc;
    }

    /**
     * This is a utility method to add a new button to the extra buttons
     * collection.
     *   
     * @param property
     * @param source
     * @param altText
     */
    protected void addExtraButton(String property, String source, String altText) {

        ExtraButton newButton = new ExtraButton();

        newButton.setExtraButtonProperty(property);
        newButton.setExtraButtonSource(source);
        newButton.setExtraButtonAltText(altText);

        extraButtons.add(newButton);
    }

    public Map<String, Boolean> getPersonEditableFields() {
        if (personEditableFields == null) {
            populatePersonEditableFields();
        }
        return personEditableFields;
    }

    public void setPersonEditableFields(Map<String, Boolean> personEditableFields) {
        this.personEditableFields = personEditableFields;
    }

    /**
     * Creates the list of <code>{@link PersonEditableField}</code> field names.
     */
    public void populatePersonEditableFields() {
        // TODO : should refactor this editablefields related to parent class, so it can be shared by other 
        // modules

        setPersonEditableFields(new HashMap<String, Boolean>());

        @SuppressWarnings("unchecked")
        Map fieldValues = new HashMap();
        fieldValues.put("moduleCode", getModuleCode());
        Collection<PersonEditableField> fields = getBusinessObjectService().findMatching(PersonEditableField.class,
                fieldValues);
        for (PersonEditableField field : fields) {
            getPersonEditableFields().put(field.getFieldName(), Boolean.valueOf(field.isActive()));
        }
    }

    private BusinessObjectService getBusinessObjectService() {
        return KcServiceLocator.getService(BusinessObjectService.class);
    }

    /**
     * 
     * This method should be overriden by modules that is using person editable field.
     * @return
     */
    public String getModuleCode() {
        return "0";
    }

    /**
     * This is a duplication of KualiTransactionalDocumentFormBase.populateFalseCheckboxes with the cavet that this function
     * puts a NULL in for fields that contain "answer", which are the field names of radio Y/N buttons for the questionnaire framework.
     * @see org.kuali.rice.kns.web.struts.form.KualiTransactionalDocumentFormBase#populateFalseCheckboxes(javax.servlet.http.HttpServletRequest)
     */
    @Override
    protected void populateFalseCheckboxes(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        final String checkBoxToResetFieldParam = "checkboxToReset";
        if (parameterMap.get(checkBoxToResetFieldParam) != null) {
            final String[] checkboxesToResetFields = request.getParameterValues("checkboxToReset");
            if (checkboxesToResetFields != null && checkboxesToResetFields.length > 0) {
                for (int i = 0; i < checkboxesToResetFields.length; i++) {
                    String propertyName = (String) checkboxesToResetFields[i];
                    if (!StringUtils.isBlank(propertyName) && parameterMap.get(propertyName) == null) {
                        if (this instanceof QuestionableFormInterface
                                && (StringUtils.startsWithIgnoreCase(propertyName,
                                        ((QuestionableFormInterface) this).getQuestionnaireFieldStarter())
                                        && StringUtils.containsIgnoreCase(propertyName,
                                                ((QuestionableFormInterface) this).getQuestionnaireFieldMiddle())
                                        && StringUtils.endsWithIgnoreCase(propertyName,
                                                ((QuestionableFormInterface) this).getQuestionnaireFieldEnd())
                                        || propertyName.matches(
                                                ((QuestionableFormInterface) this).getQuestionnaireExpression()))) {
                            populateForProperty(propertyName, null, parameterMap);
                        } else if (this instanceof MultiQuestionableFormInterface) {
                            processMultiQuestionCheckBox(propertyName, parameterMap,
                                    (MultiQuestionableFormInterface) this);

                        } else {
                            populateForProperty(propertyName,
                                    KimConstants.KIM_ATTRIBUTE_BOOLEAN_FALSE_STR_VALUE_DISPLAY, parameterMap);
                        }
                    } else if (!StringUtils.isBlank(propertyName) && parameterMap.get(propertyName) != null
                            && parameterMap.get(propertyName).length >= 1
                            && parameterMap.get(propertyName)[0].equalsIgnoreCase("on")) {
                        populateForProperty(propertyName, KimConstants.KIM_ATTRIBUTE_BOOLEAN_TRUE_STR_VALUE_DISPLAY,
                                parameterMap);
                    }
                }
            }
        }
    }

    protected void processMultiQuestionCheckBox(String propertyName, Map<String, String[]> parameterMap,
            MultiQuestionableFormInterface form) {
        boolean checkBoxFound = false;
        int j = 0;
        for (String starter : form.getQuestionnaireFieldStarters()) {
            if (!checkBoxFound && StringUtils.startsWithIgnoreCase(propertyName, starter)
                    && StringUtils.containsIgnoreCase(propertyName, form.getQuestionnaireFieldEnds()[j])
                    && StringUtils.endsWithIgnoreCase(propertyName, form.getQuestionnaireFieldEnds()[j])) {
                populateForProperty(propertyName, null, parameterMap);
                ;
                checkBoxFound = true;
                break;
            }
            j++;
        }
        if (!checkBoxFound) {
            populateForProperty(propertyName, KimConstants.KIM_ATTRIBUTE_BOOLEAN_FALSE_STR_VALUE_DISPLAY,
                    parameterMap);
        }
    }

    public List<String> getUnitRulesMessages() {
        return unitRulesMessages;
    }

    public void setUnitRulesMessages(List<String> unitRulesMessages) {
        this.unitRulesMessages = unitRulesMessages;
    }

    public boolean isUnitRulesErrorsExist() {
        return getUnitRulesErrors().size() > 0;
    }

    public boolean isUnitRulesWarningsExist() {
        return getUnitRulesWarnings().size() > 0;
    }

    public List<String> getUnitRulesErrors() {
        return getUnitRulesMessages(KcKrmsConstants.MESSAGE_TYPE_ERROR);
    }

    public List<String> getUnitRulesWarnings() {
        return getUnitRulesMessages(KcKrmsConstants.MESSAGE_TYPE_WARNING);
    }

    public void clearUnitRulesMessages() {
        this.unitRulesMessages = Collections.emptyList();
    }

    protected List<String> getUnitRulesMessages(String messageType) {
        List<String> messages = new ArrayList<String>();
        for (String message : this.unitRulesMessages) {
            if (StringUtils.substringBefore(message, KcKrmsConstants.MESSAGE_SEPARATOR).equals(messageType)) {
                messages.add(StringUtils.substringAfter(message, KcKrmsConstants.MESSAGE_SEPARATOR));
            }
        }
        return messages;
    }

    protected String getBaseShortUrl() {
        return getConfigurationService().getPropertyValueAsString("application.url");
    }

    protected ConfigurationService getConfigurationService() {
        return KcServiceLocator.getService(ConfigurationService.class);
    }
}