001/**
002 * Copyright 2005-2017 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.kew.actions;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
020import org.kuali.rice.kew.actionrequest.ActionRequestValue;
021import org.kuali.rice.kew.actionrequest.KimGroupRecipient;
022import org.kuali.rice.kew.actionrequest.KimPrincipalRecipient;
023import org.kuali.rice.kew.actionrequest.Recipient;
024import org.kuali.rice.kew.engine.node.RouteNodeInstance;
025import org.kuali.rice.kew.api.exception.InvalidActionTakenException;
026import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
027import org.kuali.rice.kew.service.KEWServiceLocator;
028import org.kuali.rice.kew.api.KewApiConstants;
029import org.kuali.rice.kim.api.identity.principal.PrincipalContract;
030import org.kuali.rice.kim.api.group.Group;
031
032
033import java.util.List;
034
035
036/**
037 * Responsible for creating adhoc requests that are requested from the client.
038 *
039 * @author Kuali Rice Team (rice.collab@kuali.org)
040 */
041public class AdHocAction extends ActionTakenEvent {
042    /**
043     * AdHoc actions don't actually result in an action being taken...it's a special case that generates other action requests
044     */
045    private static final String NO_ACTION_TAKEN_CODE = null;
046
047        private String actionRequested;
048        private String nodeName;
049    private Integer priority;
050        private String responsibilityDesc;
051        private Boolean forceAction;
052        private Recipient recipient;
053        private String annotation;
054        private String requestLabel;
055
056    public AdHocAction(DocumentRouteHeaderValue routeHeader, PrincipalContract principal) {
057        super(NO_ACTION_TAKEN_CODE, routeHeader, principal);
058    }
059
060        public AdHocAction(DocumentRouteHeaderValue routeHeader, PrincipalContract principal, String annotation, String actionRequested, String nodeName, Integer priority, Recipient recipient, String responsibilityDesc, Boolean forceAction, String requestLabel) {
061                super(NO_ACTION_TAKEN_CODE, routeHeader, principal, annotation);
062                this.actionRequested = actionRequested;
063                this.nodeName = nodeName;
064        this.priority = priority;
065                this.responsibilityDesc = responsibilityDesc;
066                this.forceAction = forceAction;
067                this.recipient = recipient;
068                this.annotation = annotation;
069                this.requestLabel = requestLabel;
070        }
071
072        public void recordAction() throws InvalidActionTakenException {
073                String errorMessage = validateActionRules();
074        if (!org.apache.commons.lang.StringUtils.isEmpty(errorMessage)) {
075            throw new InvalidActionTakenException(errorMessage);
076        }
077                List targetNodes = KEWServiceLocator.getRouteNodeService().getCurrentNodeInstances(getDocumentId());
078        String error = adhocRouteAction(targetNodes, false);
079        if (!org.apache.commons.lang.StringUtils.isEmpty(error)) {
080            throw new InvalidActionTakenException(error);
081        }
082    }
083
084    /* (non-Javadoc)
085     * @see org.kuali.rice.kew.actions.ActionTakenEvent#validateActionRules()
086     */
087    @Override
088    public String validateActionRules() {
089        List targetNodes = KEWServiceLocator.getRouteNodeService().getCurrentNodeInstances(getDocumentId());
090        return validateActionRulesInternal(targetNodes);
091    }
092    
093    @Override
094    public String validateActionRules(List<ActionRequestValue> actionRequests) {
095        return validateActionRules();
096    }
097
098    private String validateActionRulesInternal(List<RouteNodeInstance> targetNodes) {
099        // recipient will be null when this is invoked from ActionRegistry.getValidActions
100        if (recipient != null) {
101                if (recipient instanceof KimPrincipalRecipient) {
102                        KimPrincipalRecipient principalRecipient = (KimPrincipalRecipient)recipient;
103                        if (!KEWServiceLocator.getDocumentTypePermissionService().canReceiveAdHocRequest(principalRecipient.getPrincipalId(), getRouteHeader(), actionRequested)) {
104                                return "The principal '" + principalRecipient.getPrincipal().getPrincipalName() + "' does not have permission to recieve ad hoc requests on DocumentType '" + getRouteHeader().getDocumentType().getName() + "'";
105                        }
106                } else if (recipient instanceof KimGroupRecipient) {
107                        Group group = ((KimGroupRecipient)recipient).getGroup();
108                        if (!KEWServiceLocator.getDocumentTypePermissionService().canGroupReceiveAdHocRequest("" + group.getId(), getRouteHeader(), actionRequested)) {
109                                return "The group '" + group.getName() + "' does not have permission to recieve ad hoc requests on DocumentType '" + getRouteHeader().getDocumentType().getName() + "'";
110                        }
111                } else {
112                        return "Invalid Recipient type encountered: " + recipient.getClass();
113                }
114        }
115        return adhocRouteAction(targetNodes, true);
116    }
117
118    private String adhocRouteAction(List<RouteNodeInstance> targetNodes, boolean forValidationOnly) {
119        //if (targetNodes.isEmpty()) {
120        //    return "Could not locate an node instance on the document with the name '" + nodeName + "'";
121        //}
122        boolean requestCreated = false;
123        for (RouteNodeInstance routeNode : targetNodes)
124        {
125            // if the node name is null, then adhoc it to the first available node
126            if (nodeName == null || routeNode.getName().equals(nodeName))
127            {
128                String message = createAdHocRequest(routeNode, forValidationOnly);
129                if (StringUtils.isNotBlank(message))
130                {
131                    return message;
132                }
133                requestCreated = true;
134                if (nodeName == null)
135                {
136                    break;
137                }
138            }
139        }
140        
141        if (!requestCreated && targetNodes.isEmpty()) {
142            String message = createAdHocRequest(null, forValidationOnly);
143            if (StringUtils.isNotBlank(message)) {
144                return message;
145            }
146            requestCreated = true;
147        }
148        
149        if (!requestCreated) {
150            return "Didn't create request.  The node name " + nodeName + " given is probably invalid ";
151        }
152        return "";
153    }
154    
155    private String createAdHocRequest(RouteNodeInstance routeNode, boolean forValidationOnly) {
156                ActionRequestValue adhocRequest = new ActionRequestValue();
157                if (!forValidationOnly) {
158                    ActionRequestFactory arFactory = new ActionRequestFactory(routeHeader, routeNode);
159                    adhocRequest = arFactory.createActionRequest(actionRequested, recipient, responsibilityDesc, forceAction, annotation);
160                    adhocRequest.setResponsibilityId(KewApiConstants.ADHOC_REQUEST_RESPONSIBILITY_ID);
161                    adhocRequest.setRequestLabel(requestLabel);
162                    if (priority != null) {
163                        adhocRequest.setPriority(priority);
164                    }
165                } else {
166                    adhocRequest.setActionRequested(actionRequested);
167                }
168                if (adhocRequest.isApproveOrCompleteRequest() && ! (routeHeader.isEnroute() || routeHeader.isStateInitiated() ||
169                        routeHeader.isStateSaved())) {
170            return "Cannot AdHoc a Complete or Approve request when document is in state '" + routeHeader.getDocRouteStatusLabel() + "'.";
171                }
172                if (!forValidationOnly) {
173     
174                        if (routeHeader.isDisaproved() || routeHeader.isCanceled() || routeHeader.isFinal() || routeHeader.isProcessed()) {
175                        getActionRequestService().activateRequest(adhocRequest);
176                    } else {
177                        KEWServiceLocator.getActionRequestService().saveActionRequest(adhocRequest);
178                    }
179                }
180        return "";
181    }
182}