org.kuali.kra.common.permissions.web.struts.action.PermissionsActionHelperBase.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kra.common.permissions.web.struts.action.PermissionsActionHelperBase.java

Source

/*
 * Copyright 2005-2010 The Kuali Foundation
 * 
 * Licensed under the Educational Community 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.opensource.org/licenses/ecl1.php
 * 
 * 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.kuali.kra.common.permissions.web.struts.action;

import static org.apache.commons.lang.StringUtils.replace;
import static org.kuali.kra.infrastructure.KraServiceLocator.getService;
import static org.kuali.rice.kns.util.KNSConstants.CONFIRMATION_QUESTION;
import static org.kuali.rice.kns.util.KNSConstants.QUESTION_INST_ATTRIBUTE_NAME;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.kuali.kra.common.permissions.bo.PermissionsRoleState;
import org.kuali.kra.common.permissions.bo.PermissionsUser;
import org.kuali.kra.common.permissions.bo.PermissionsUserEditRoles;
import org.kuali.kra.common.permissions.rule.event.AddPermissionsUserEvent;
import org.kuali.kra.common.permissions.rule.event.DeletePermissionsUserEvent;
import org.kuali.kra.common.permissions.rule.event.EditUserPermissionsRolesEvent;
import org.kuali.kra.common.permissions.web.bean.Role;
import org.kuali.kra.common.permissions.web.bean.RoleState;
import org.kuali.kra.common.permissions.web.bean.User;
import org.kuali.kra.common.permissions.web.bean.UserState;
import org.kuali.kra.common.permissions.web.struts.form.PermissionsForm;
import org.kuali.kra.common.permissions.web.struts.form.PermissionsHelperBase;
import org.kuali.kra.infrastructure.Constants;
import org.kuali.kra.infrastructure.KeyConstants;
import org.kuali.kra.irb.ProtocolDocument;
import org.kuali.kra.web.struts.action.KraTransactionalDocumentActionBase;
import org.kuali.kra.web.struts.action.StrutsConfirmation;
import org.kuali.rice.kns.document.Document;
import org.kuali.rice.kns.rule.event.KualiDocumentEvent;
import org.kuali.rice.kns.service.KualiConfigurationService;
import org.kuali.rice.kns.service.KualiRuleService;
import org.kuali.rice.kns.util.KNSConstants;

/**
 * The PermissionsActionHelperBase is a delegate for supporting the Permissions Tab web page.
 * A subclass is necessary to perform operations specific to the document and roles for the 
 * Permissions web page, e.g. saving users and roles to the database.
 */
public abstract class PermissionsActionHelperBase implements Serializable {

    private static final String ADD_USER_METHOD = "addUser";
    private static final String DELETE_USER_METHOD = "deleteUser";
    private static final String SET_EDIT_ROLES_METHOD = "setEditRoles";

    private KraTransactionalDocumentActionBase parentAction;

    protected PermissionsActionHelperBase(KraTransactionalDocumentActionBase parentAction) {
        this.parentAction = parentAction;
    }

    /**
     * Assign a user to the given role for a specific document.
     * @param document the document
     * @param userName the user's unique userName
     * @param roleName the unique name of the role
     */
    protected abstract void addUserToRoleInDatabase(Document document, String userId, String roleName);

    /**
     * Remove a user from a role for a specific document.
     * @param document the document
     * @param userName the user's unique userName
     * @param roleName the unique name of the role
     */
    protected abstract void removeUserFromRoleInDatabase(Document document, String userId, String roleName);

    /**
     * Save the changes to the database.  All of the changes involve
     * either adding or removing roles for a user.
     * @param form the form
     * @throws Exception
     */
    public void save(PermissionsForm form) throws Exception {
        List<UserState> userStates = form.getPermissionsHelper().getUserStates();
        removeRolesFromUsers(form.getDocument(), userStates);
        addRolesToUsers(form.getDocument(), userStates);
        removeUsersWithNoRoles(userStates);
    }

    /*
     * If a user no longer has a given role, remove it.  Also update its
     * current state to false to indicate that it has been removed from
     * the database.  This is necessary for any future changes to users 
     * and their roles. 
     */
    private void removeRolesFromUsers(Document document, List<UserState> userStates) {
        for (UserState userState : userStates) {
            List<RoleState> roleStates = userState.getRoleStates();
            for (RoleState roleState : roleStates) {
                if (roleState.needsToBeRemoved()) {
                    removeUserFromRoleInDatabase(document, userState.getPerson().getPersonId(),
                            roleState.getRole().getName());
                    roleState.setSaved(false);
                }
            }
        }
    }

    /*
     * If a role has been assigned a role but it is not yet in the database,
     * then make the change to the database.  Also update its
     * current state to true to indicate that it has been added to
     * the database.  This is necessary for any future changes to users 
     * and their roles. 
     */
    private void addRolesToUsers(Document document, List<UserState> userStates) {
        for (UserState userState : userStates) {
            List<RoleState> roleStates = userState.getRoleStates();
            for (RoleState roleState : roleStates) {
                if (roleState.needsToBeAdded()) {
                    addUserToRoleInDatabase(document, userState.getPerson().getPersonId(),
                            roleState.getRole().getName());
                    roleState.setSaved(true);
                }
            }
        }
    }

    /*
     * If a user has been deleted from the document, then he/she won't
     * have any more roles for that document.  If so, we can remove
     * that user from the list of userStates.  NOTE: For Java newbies
     * please note that the for-each loop cannot be used to traverse
     * a list and remove entries; an iterator must be used.
     */
    private void removeUsersWithNoRoles(List<UserState> userStates) {
        Iterator<UserState> iter = userStates.iterator();
        while (iter.hasNext()) {
            UserState userState = iter.next();
            if (!userState.hasAnyRole()) {
                iter.remove();
            }
        }
    }

    /**
     * Get the Role Rights web page.
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward getRoleRights(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        return mapping.findForward(Constants.MAPPING_PERMISSIONS_ROLE_RIGHTS_PAGE);
    }

    /**
     * Add a new user with a role to the document.
     * @param mapping 
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public final ActionForward addUser(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        PermissionsForm permissionsForm = (PermissionsForm) form;
        PermissionsHelperBase permissionsHelper = permissionsForm.getPermissionsHelper();
        Document doc = permissionsForm.getDocument();

        // early exit if the end-user is not authorized
        if (!permissionsHelper.canModifyPermissions()) {
            return parentAction.processAuthorizationViolation(ADD_USER_METHOD, mapping, form, request, response);
        }

        PermissionsUser newUser = permissionsForm.getPermissionsHelper().getNewUser();
        boolean rulePassed = applyRules(
                new AddPermissionsUserEvent(doc, permissionsForm.getPermissionsHelper().getUsers(), newUser));
        if (rulePassed) {
            permissionsForm.getPermissionsHelper().addNewUser();
        }
        return mapping.findForward(Constants.MAPPING_BASIC);
    }

    /**
     * Delete a user and his/her roles from the document.
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public final ActionForward deleteUser(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        ActionForward actionForward = mapping.findForward(Constants.MAPPING_BASIC);

        PermissionsForm permissionsForm = (PermissionsForm) form;
        PermissionsHelperBase permissionsHelper = permissionsForm.getPermissionsHelper();
        Document doc = permissionsForm.getDocument();

        // early exit if not authorized
        if (!permissionsHelper.canModifyPermissions()) {
            actionForward = parentAction.processAuthorizationViolation(DELETE_USER_METHOD, mapping, form, request,
                    response);
        } else {

            // The lineNum is the index into "users" of the user to be deleted.
            int lineNum = getLineToDelete(request);
            List<User> users = permissionsForm.getPermissionsHelper().getUsers();

            boolean rulePassed = applyRules(new DeletePermissionsUserEvent(doc, users, lineNum));
            if (rulePassed) {
                // ask for a confirmation before deleting the user from the list
                actionForward = parentAction.confirm(
                        buildDeleteUserConfirmationQuestion(mapping, form, request, response),
                        Constants.CONFIRM_DELETE_PERMISSIONS_USER_KEY, "");
            }
        }

        return actionForward;
    }

    /**
     * If the the end-user confirms that a user must be deleted from the list, then do so.
     * @param mapping the mapping associated with this action.
     * @param form the form.
     * @param request the HTTP request
     * @param response the HTTP response
     * @return the Permissions web page
     * @throws Exception
     */
    public ActionForward confirmDeletePermissionsUser(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        Object question = request.getParameter(QUESTION_INST_ATTRIBUTE_NAME);
        if (Constants.CONFIRM_DELETE_PERMISSIONS_USER_KEY.equals(question)) {
            PermissionsForm permissionsForm = (PermissionsForm) form;
            int lineNum = getLineToDelete(request);
            User user = permissionsForm.getPermissionsHelper().getUsers().get(lineNum);
            UserState userState = permissionsForm.getPermissionsHelper()
                    .getUserState(user.getPerson().getUserName());
            if (userState != null) {
                userState.clearAssignments();
            }
        }

        return mapping.findForward(Constants.MAPPING_BASIC);
    }

    /*
     * Build the confirmation question that will be asked of the end-user 
     * before deleting a user from the list.
     */
    private StrutsConfirmation buildDeleteUserConfirmationQuestion(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        PermissionsForm permissionsForm = (PermissionsForm) form;
        String fullname = permissionsForm.getPermissionsHelper().getUsers().get(getLineToDelete(request))
                .getPerson().getFullName();
        return buildParameterizedConfirmationQuestion(mapping, form, request, response,
                Constants.CONFIRM_DELETE_PERMISSIONS_USER_KEY,
                KeyConstants.QUESTION_DELETE_PROPOSAL_USER_CONFIRMATION, fullname);
    }

    /*
     * Generically creates a <code>{@link StrutsConfirmation}</code> instance while deriving the question from a resource bundle.<br/>
     * <br/> In this case, the question in the resource bundle is expected to be parameterized. This method takes this into account,
     * and passes parameters and replaces tokens in the question with the parameters.
     */
    private StrutsConfirmation buildParameterizedConfirmationQuestion(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response, String questionId, String configurationId,
            String... params) throws Exception {

        StrutsConfirmation retval = new StrutsConfirmation();
        retval.setMapping(mapping);
        retval.setForm(form);
        retval.setRequest(request);
        retval.setResponse(response);
        retval.setQuestionId(questionId);
        retval.setQuestionType(CONFIRMATION_QUESTION);

        KualiConfigurationService kualiConfiguration = getService(KualiConfigurationService.class);
        String questionText = kualiConfiguration.getPropertyString(configurationId);

        for (int i = 0; i < params.length; i++) {
            questionText = replace(questionText, "{" + i + "}", params[i]);
        }
        retval.setQuestionText(questionText);

        return retval;
    }

    /**
     * Edit the roles for a user.
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public ActionForward editRoles(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        PermissionsForm permissionsForm = (PermissionsForm) form;
        PermissionsHelperBase permissionsHelper = permissionsForm.getPermissionsHelper();

        int lineNum = getLineNum(request);
        User user = permissionsHelper.getUsers().get(lineNum);

        /*
         * Create an Edit Roles BO that will be used by the form for setting
         * the boolean flags for the roles. 
         */
        PermissionsUserEditRoles editRoles = new PermissionsUserEditRoles();
        editRoles.setLineNum(lineNum);
        editRoles.setJavaScriptEnabled(isJavaScriptEnabled(request));
        editRoles.setUserName(user.getPerson().getUserName());
        editRoles.setPrinipalInvestigator(isPrincipalInvestigator((ProtocolDocument) permissionsForm.getDocument(),
                user.getPerson().getPersonId()));

        List<PermissionsRoleState> roleStates = new ArrayList<PermissionsRoleState>();
        List<Role> roles = permissionsHelper.getNormalRoles();
        for (Role role : roles) {
            PermissionsRoleState roleState = new PermissionsRoleState(role);
            roleStates.add(roleState);
        }
        editRoles.setRoleStates(roleStates);

        /*
         * Initialize the Edit Roles BO to the roles that the user is currently assigned to.
         */
        List<Role> userRoles = user.getRoles();
        for (Role userRole : userRoles) {
            editRoles.setRoleState(userRole.getName(), Boolean.TRUE);
        }

        permissionsHelper.setUserEditRoles(editRoles);

        return mapping.findForward(Constants.MAPPING_PERMISSIONS_EDIT_ROLES_PAGE);
    }

    /**
     * Set the roles for a user for a given document.  The setEditRoles() method works in conjunction
     * with the above editRoles() method.  The editRoles() method causes the Edit Roles web page to
     * be displayed.  The setEditRoles() is invoked when the user clicks on the save button.  Note that
     * the Edit Roles BO created in editRoles() is retrieved in setEditRoles() to determine the user
     * and what the new roles should be.
     * 
     * @param mapping the mapping associated with this action.
     * @param form the form.
     * @param request the HTTP request
     * @param response the HTTP response
     * @return the next web page to display
     * @throws Exception
     */
    public ActionForward setEditRoles(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        ActionForward actionForward = null;

        PermissionsHelperBase permissionsHelper = ((PermissionsForm) form).getPermissionsHelper();
        PermissionsForm permissionsForm = (PermissionsForm) form;
        Document doc = permissionsForm.getDocument();

        // early exit if not authorized
        if (!permissionsHelper.canModifyPermissions()) {
            return parentAction.processAuthorizationViolation(SET_EDIT_ROLES_METHOD, mapping, form, request,
                    response);
        }

        // The Edit Roles BO contains the username, javascriptEnabled, and the new set of
        // roles to set for the user in the document.

        PermissionsUserEditRoles editRoles = permissionsHelper.getEditRoles();

        // check any business rules
        boolean rulePassed = applyRules(
                new EditUserPermissionsRolesEvent(doc, permissionsHelper.getUsers(), editRoles));
        if (!rulePassed) {

            // If the user can't perform the save of the roles, then we will report the
            // error on the Edit Roles web page.  This allows the user to fix the error
            // and re-submit the save.

            actionForward = mapping.findForward(Constants.MAPPING_PERMISSIONS_EDIT_ROLES_PAGE);
        } else {
            updateRoles(editRoles, permissionsHelper);

            // If Javascript was enabled, we can simply cause the pop-up window to close.
            // If not, then we must return the user to the Permissions page.

            if (editRoles.getJavaScriptEnabled()) {
                actionForward = mapping.findForward(Constants.MAPPING_PERMISSIONS_CLOSE_EDIT_ROLES_PAGE);
            } else {
                actionForward = mapping.findForward(Constants.MAPPING_BASIC);
            }
        }

        return actionForward;
    }

    /*
     * Update the roles for the user.
     * @param editRoles the new set of roles for the user
     * @param permissionsHelper the PermissionsHelper
     */
    private void updateRoles(PermissionsUserEditRoles editRoles, PermissionsHelperBase permissionsHelper) {

        /*
         * Clear the current roles before setting the new ones
         */
        UserState userState = permissionsHelper.getUserState(editRoles.getUserName());
        userState.clearAssignments();

        /*
         * Set the new roles.
         */
        List<PermissionsRoleState> roleStates = editRoles.getRoleStates();
        for (PermissionsRoleState roleState : roleStates) {
            if (roleState.getState()) {
                userState.setAssigned(roleState.getRole().getName(), true);
            }
        }

        // If the user isn't assigned to any of the normal roles, then he/she will
        // be assigned to the unassigned role.  

        if (!userState.hasAnyRole() && permissionsHelper.getUnassignedRoleName() != null) {
            userState.setAssigned(permissionsHelper.getUnassignedRoleName(), true);
        }
    }

    /*
     * Get the line number of the user to operate on.  
     * 
     * @param request the HTTP request
     * @return the line number
     */
    private int getLineNum(HttpServletRequest request) {

        // If JavaScript is enabled, the line is returned to the web server
        // as an HTTP parameter.  If not, it is embedded within the "methodToCall" syntax.

        String lineNumStr = request.getParameter("line");
        try {
            return Integer.parseInt(lineNumStr);
        } catch (Exception ex) {
            return this.getLineToDelete(request);
        }
    }

    /*
     * Is JavaScript enabled on the user's browser?  This implementation is
     * a bit of a hack.  When JavaScript is enabled, the line is returned
     * to the web server as an HTTP request.  If not, it is embedded with
     * the "methodToCall" syntax.
     * 
     * @param request the HTTP request
     * @return true if JavaScript is enabled; otherwise false
     */
    private boolean isJavaScriptEnabled(HttpServletRequest request) {
        String lineNumStr = request.getParameter("line");
        try {
            Integer.parseInt(lineNumStr);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }

    /*
     * Parses the method to call attribute to pick off the line number which should be deleted.
     */
    private int getLineToDelete(HttpServletRequest request) {
        return getSelectedLine(request);
    }

    /*
     * Parses the method to call attribute to pick off the line number which should have an action performed on it.
     */
    private int getSelectedLine(HttpServletRequest request) {
        int selectedLine = -1;
        String parameterName = (String) request.getAttribute(KNSConstants.METHOD_TO_CALL_ATTRIBUTE);
        if (StringUtils.isNotBlank(parameterName)) {
            String lineNumber = StringUtils.substringBetween(parameterName, ".line", ".");
            selectedLine = Integer.parseInt(lineNumber);
        }

        return selectedLine;
    }

    /*
     * Get the Kuali Rule Service.
     */
    private KualiRuleService getKualiRuleService() {
        return getService(KualiRuleService.class);
    }

    /*
     * Use the Kuali Rule Service to apply the rules for the given event.
     */
    protected final boolean applyRules(KualiDocumentEvent event) {
        return getKualiRuleService().applyRules(event);
    }

    /*
     * Check if user is PI
     */
    private boolean isPrincipalInvestigator(ProtocolDocument protocolDocument, String personId) {
        return StringUtils.equals(personId, protocolDocument.getProtocol().getPrincipalInvestigatorId());
    }
}