View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.rule.web;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.log4j.Logger;
20  import org.apache.struts.action.ActionForm;
21  import org.apache.struts.action.ActionForward;
22  import org.apache.struts.action.ActionMapping;
23  import org.apache.struts.action.ActionMessages;
24  import org.kuali.rice.core.api.config.property.ConfigContext;
25  import org.kuali.rice.core.api.criteria.Predicate;
26  import org.kuali.rice.core.api.criteria.QueryByCriteria;
27  import org.kuali.rice.kew.doctype.bo.DocumentType;
28  import org.kuali.rice.kew.doctype.service.DocumentTypeService;
29  import org.kuali.rice.kew.engine.node.BranchPrototype;
30  import org.kuali.rice.kew.engine.node.RouteNode;
31  import org.kuali.rice.kew.engine.node.RouteNodeConfigParam;
32  import org.kuali.rice.kew.rule.RuleBaseValues;
33  import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
34  import org.kuali.rice.kew.rule.service.RuleServiceInternal;
35  import org.kuali.rice.kew.service.KEWServiceLocator;
36  import org.kuali.rice.kew.api.KewApiConstants;
37  import org.kuali.rice.kew.web.KewKualiAction;
38  import org.kuali.rice.kim.api.KimConstants;
39  import org.kuali.rice.kim.api.group.Group;
40  import org.kuali.rice.kim.api.permission.Permission;
41  import org.kuali.rice.kim.api.responsibility.Responsibility;
42  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
43  import org.kuali.rice.kns.service.DocumentHelperService;
44  import org.kuali.rice.kns.service.KNSServiceLocator;
45  import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
46  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
47  import org.kuali.rice.krad.util.GlobalVariables;
48  import org.kuali.rice.krad.util.KRADConstants;
49  
50  import javax.servlet.http.HttpServletRequest;
51  import javax.servlet.http.HttpServletResponse;
52  import java.util.ArrayList;
53  import java.util.Collections;
54  import java.util.Comparator;
55  import java.util.HashMap;
56  import java.util.Iterator;
57  import java.util.List;
58  
59  import static org.kuali.rice.core.api.criteria.PredicateFactory.and;
60  import static org.kuali.rice.core.api.criteria.PredicateFactory.equal;
61  
62  
63  /**
64   * A Struts Action for building and interacting with the Rule Quick Links.
65   *
66   * @author Kuali Rice Team (rice.collab@kuali.org)
67   */
68  public class RuleQuickLinksAction extends KewKualiAction {
69  
70  	private static final Logger LOG = Logger.getLogger(RuleQuickLinksAction.class);
71  	
72  	private MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
73  	private DocumentHelperService documentHelperService;
74  
75      public ActionForward start(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
76      	makeLookupPathParam(mapping, request);
77      	establishRequiredState(request, form);
78          return mapping.findForward("basic");
79      }
80  
81      @SuppressWarnings("unchecked")
82  	public ActionMessages establishRequiredState(HttpServletRequest request, ActionForm form) throws Exception {
83          RuleQuickLinksForm qlForm = (RuleQuickLinksForm) form;
84          List<DocumentType> documentTypes;
85          if (qlForm.getRootDocTypeName() != null) {
86          	documentTypes = new ArrayList<DocumentType>();
87              DocumentType docType = getDocumentTypeService().findByName(qlForm.getRootDocTypeName());
88              documentTypes.add(docType);
89              request.setAttribute("renderOpened", Boolean.TRUE);
90          } else {
91          	documentTypes = getDocumentTypeService().findAllCurrentRootDocuments();
92          }
93          qlForm.setDocumentTypeQuickLinksStructures(getDocumentTypeDataStructure(documentTypes));
94      	int shouldDisplayCount = 0;
95          for ( DocumentTypeQuickLinksStructure dt : qlForm.getDocumentTypeQuickLinksStructures() ) {
96          	if ( dt.isShouldDisplay() ) {
97          		shouldDisplayCount++;
98          	}
99          }
100     	if ( shouldDisplayCount == 1 ) {
101             request.setAttribute("renderOpened", Boolean.TRUE);
102     	}
103         String documentTypeName = getMaintenanceDocumentDictionaryService().getDocumentTypeName(DocumentType.class);
104         try {
105             if ((documentTypeName != null) && getDocumentHelperService().getDocumentAuthorizer(documentTypeName).canInitiate(documentTypeName, GlobalVariables.getUserSession().getPerson())) {
106                 qlForm.setCanInitiateDocumentTypeDocument( true );
107             }
108         } catch (Exception ex) {
109 			// just skip - and don't display links
110         	LOG.error( "Unable to check initiation permission for "+ documentTypeName, ex );
111 		}
112 
113         return null;
114     }
115 
116     public ActionForward addDelegationRule(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
117     	ActionForward result = null;
118 
119         String ruleTemplateId = request.getParameter("delegationRule.ruleTemplate.id");
120         String docTypeName = request.getParameter("delegationRule.documentType.name");
121         List rules = getRuleService().search(docTypeName, null, ruleTemplateId, "", null, null, Boolean.FALSE, Boolean.TRUE, new HashMap(), null);
122         
123         if (rules.size() == 1) {
124             RuleBaseValues rule = (RuleBaseValues)rules.get(0);
125             String url = ConfigContext.getCurrentContextConfig().getKEWBaseURL() +
126                 "/DelegateRule.do?methodToCall=start" +
127             	"&parentRuleId=" + rule.getId();
128             result = new ActionForward(url, true);
129         } else {
130         	makeLookupPathParam(mapping, request);
131         	result = new ActionForward(ConfigContext.getCurrentContextConfig().getKRBaseURL() + 
132         			"/lookup.do?methodToCall=start&"+ stripMethodToCall(request.getQueryString()), true);
133         }
134         
135         return result;
136     }
137 
138 	private List getDocumentTypeDataStructure(List rootDocuments) {
139 		List documentTypeQuickLinksStructures = new ArrayList();
140 		for (Iterator iter = rootDocuments.iterator(); iter.hasNext();) {
141 			DocumentTypeQuickLinksStructure quickLinkStruct =new DocumentTypeQuickLinksStructure((DocumentType) iter.next());
142 			if (! quickLinkStruct.getFlattenedNodes().isEmpty() || ! quickLinkStruct.getChildrenDocumentTypes().isEmpty()) {
143 				documentTypeQuickLinksStructures.add(quickLinkStruct);
144 			}
145 
146 		}
147 
148 		return documentTypeQuickLinksStructures;
149 	}
150 
151 
152 	/**
153      * A bean to hold a DocumentType with its flattened nodes for rendering purposes
154      * on the quick links.
155      *
156  * @author Kuali Rice Team (rice.collab@kuali.org)
157      */
158     public static class DocumentTypeQuickLinksStructure {
159         private DocumentType documentType;
160         private List<RouteNode> flattenedNodes = new ArrayList<RouteNode>();
161         private List<DocumentTypeQuickLinksStructure> childrenDocumentTypes = new ArrayList<DocumentTypeQuickLinksStructure>();
162         private List<Permission> permissions = null;
163         
164         private DocumentTypeQuickLinksStructure(DocumentType documentType) {
165 			this.documentType = documentType;
166 			if ( documentType != null ) {
167 				List<RouteNode> tempFlattenedNodes = KEWServiceLocator.getRouteNodeService()
168 						.getFlattenedNodes( documentType, true );
169 				for ( RouteNode routeNode : tempFlattenedNodes ) {
170 					if ( routeNode.isFlexRM() || routeNode.isRoleNode() ) {
171 						flattenedNodes.add( new RouteNodeForDisplay( routeNode ) );
172 					}
173 				}
174                 //KULRICE-12357: Reverting change so that only document types are in alpha order
175                 /*Collections.sort(flattenedNodes,new Comparator() {
176                     public int compare(Object o1, Object o2) {
177                         return ( ((RouteNode)o1).getRouteNodeName().compareTo(((RouteNode)o2).getRouteNodeName()));
178                     }
179                 });*/
180 				for ( Iterator<DocumentType> iter = documentType.getChildrenDocTypes().iterator(); iter.hasNext(); ) {
181 					childrenDocumentTypes.add( new DocumentTypeQuickLinksStructure( iter.next() ) );
182 				}
183                 Collections.sort(childrenDocumentTypes,new Comparator() {
184                     public int compare(Object o1, Object o2) {
185                          return ( ((DocumentTypeQuickLinksStructure)o1).documentType.getLabel().compareTo(((DocumentTypeQuickLinksStructure)o2).documentType.getLabel()));
186                     }
187                 });
188 			}
189 		}
190 
191         public List getChildrenDocumentTypes() {
192             return childrenDocumentTypes;
193         }
194         public void setChildrenDocumentTypes(List<DocumentTypeQuickLinksStructure> childrenDocumentTypes) {
195             this.childrenDocumentTypes = childrenDocumentTypes;
196         }
197         public DocumentType getDocumentType() {
198             return documentType;
199         }
200         public void setDocumentType(DocumentType documentType) {
201             this.documentType = documentType;
202         }
203         public List getFlattenedNodes() {
204             return flattenedNodes;
205         }
206         public void setFlattenedNodes(List<RouteNode> flattenedNodes) {
207             this.flattenedNodes = flattenedNodes;
208         }
209         public boolean isShouldDisplay() {
210 //			if (flattenedNodes.isEmpty()) {
211 //				for (DocumentTypeQuickLinksStructure docType : childrenDocumentTypes) {
212 //					if (docType.isShouldDisplay()) {
213 //						return true;
214 //					}
215 //				}
216 //				return false;
217 //			}
218 			return true;
219 		}
220 
221 		public List<Permission> getPermissions() {
222 			if ( permissions == null ) {
223                 Predicate p = and(
224                         equal("attributeName", "documentTypeName"),
225                         equal("active", Boolean.TRUE),
226                         equal("detailCriteria",
227 						KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME+"="+getDocumentType().getName()));
228 				permissions = KimApiServiceLocator.getPermissionService().findPermissions( QueryByCriteria.Builder.fromPredicates(p)).getResults();
229 //				sqlLogger.setLevel( Level.INFO );
230 			}
231 			return permissions;
232 		}
233 		
234 		public boolean isHasRelatedPermissions() {
235 			return !getPermissions().isEmpty();
236 		}
237 		
238 		public int getRelatedPermissionCount() {
239 			return getPermissions().size();
240 		}
241     }
242 
243     public static class RouteNodeForDisplay extends RouteNode {
244     	private static final long serialVersionUID = 1L;
245 		private RouteNode baseNode;
246     	
247 		public RouteNodeForDisplay( RouteNode baseNode ) {
248 			this.baseNode = baseNode;
249 		}
250 
251 		public boolean equals(Object obj) {
252 			return this.baseNode.equals(obj);
253 		}
254 
255 		public String getActivationType() {
256 			return this.baseNode.getActivationType();
257 		}
258 
259 		public BranchPrototype getBranch() {
260 			return this.baseNode.getBranch();
261 		}
262 
263 		public List<RouteNodeConfigParam> getConfigParams() {
264 			return this.baseNode.getConfigParams();
265 		}
266 
267 		public String getContentFragment() {
268 			return this.baseNode.getContentFragment();
269 		}
270 
271 		public DocumentType getDocumentType() {
272 			return this.baseNode.getDocumentType();
273 		}
274 
275 		public String getDocumentTypeId() {
276 			return this.baseNode.getDocumentTypeId();
277 		}
278 		public Group getExceptionWorkgroup() {
279 			return this.baseNode.getExceptionWorkgroup();
280 		}
281 		public String getExceptionWorkgroupId() {
282 			return this.baseNode.getExceptionWorkgroupId();
283 		}
284 		public String getExceptionWorkgroupName() {
285 			return this.baseNode.getExceptionWorkgroupName();
286 		}
287 		public Boolean getFinalApprovalInd() {
288 			return this.baseNode.getFinalApprovalInd();
289 		}
290 		public Integer getLockVerNbr() {
291 			return this.baseNode.getLockVerNbr();
292 		}
293 		public Boolean getMandatoryRouteInd() {
294 			return this.baseNode.getMandatoryRouteInd();
295 		}
296 		public List<RouteNode> getNextNodes() {
297 			return this.baseNode.getNextNodes();
298 		}
299 		public String getNodeType() {
300 			return this.baseNode.getNodeType();
301 		}
302 		public List<RouteNode> getPreviousNodes() {
303 			return this.baseNode.getPreviousNodes();
304 		}
305 		public String getRouteMethodCode() {
306 			return this.baseNode.getRouteMethodCode();
307 		}
308 		public String getRouteMethodName() {
309 			return this.baseNode.getRouteMethodName();
310 		}
311 		public String getRouteNodeId() {
312 			return this.baseNode.getRouteNodeId();
313 		}
314 		public String getRouteNodeName() {
315 			return this.baseNode.getRouteNodeName();
316 		}
317 		public RuleTemplateBo getRuleTemplate() {
318 			return this.baseNode.getRuleTemplate();
319 		}
320 		public int hashCode() {
321 			return this.baseNode.hashCode();
322 		}
323 		public boolean isExceptionGroupDefined() {
324 			return this.baseNode.isExceptionGroupDefined();
325 		}
326 		public boolean isFlexRM() {
327 			return this.baseNode.isFlexRM();
328 		}
329 		public boolean isRoleNode() {
330 			return this.baseNode.isRoleNode();
331 		}
332 		public String toString() {
333 			return this.baseNode.toString();
334 		}
335 		
336 		private List<Responsibility> responsibilities = null;
337 		
338 		public List<Responsibility> getResponsibilities() {
339 			if ( responsibilities == null ) {
340 				QueryByCriteria.Builder builder = QueryByCriteria.Builder.create();
341                 Predicate p = and(
342                     equal("template.namespaceCode", KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE),
343                     equal("template.name", KewApiConstants.DEFAULT_RESPONSIBILITY_TEMPLATE_NAME),
344                     equal("active", Boolean.TRUE),
345                     equal("attributes[documentTypeName]", getDocumentType().getName())
346                     // KULRICE-8538 -- Check the route node by looping through the results below.  If it is added
347                     // into the predicate, no rows are ever returned.
348                     // equal("attributes[routeNodeName]", getRouteNodeName())
349                 );
350 
351 
352                 builder.setPredicates(p);
353 
354                 List<Responsibility> possibleResponsibilities =
355                     KimApiServiceLocator.getResponsibilityService().findResponsibilities(builder.build()).getResults();
356 
357                 if ( !possibleResponsibilities.isEmpty() ) {
358                     for ( Responsibility resp : possibleResponsibilities ) {
359                         String routeNodeName = resp.getAttributes().get( KimConstants.AttributeConstants.ROUTE_NODE_NAME);
360                         if (StringUtils.isNotEmpty(routeNodeName) && StringUtils.equals(routeNodeName, getRouteNodeName())){
361                              responsibilities.add(resp);
362                         }
363                     }
364                 } else {
365                     responsibilities = possibleResponsibilities;
366                 }
367 			}
368 			return responsibilities;
369 		}
370 		
371 		public int getResponsibilityCount() {
372 			return getResponsibilities().size();
373 		}
374 		
375 		public boolean isHasResponsibility() {
376 			return !getResponsibilities().isEmpty();
377 		}
378     }
379     
380     private void makeLookupPathParam(ActionMapping mapping, HttpServletRequest request) {
381     	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + mapping.getModuleConfig().getPrefix();
382     	request.setAttribute("basePath", basePath);
383     }
384 
385     private String stripMethodToCall(String queryString) {
386         return queryString.replaceAll("methodToCall=addDelegationRule&", "");
387     }
388 
389     private DocumentTypeService getDocumentTypeService() {
390         return KEWServiceLocator.getDocumentTypeService();
391     }
392 
393     private RuleServiceInternal getRuleService() {
394     	return KEWServiceLocator.getRuleService();
395     }
396     
397 	public DocumentHelperService getDocumentHelperService() {
398 		if(documentHelperService == null){
399 			documentHelperService = KNSServiceLocator.getDocumentHelperService();
400 		}
401 		return documentHelperService;
402 	}
403 
404 	public MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() {
405 		if(maintenanceDocumentDictionaryService == null){
406 			maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
407 		}
408 		return maintenanceDocumentDictionaryService;
409 	}
410 
411 	@Override
412 	public ActionForward toggleTab(ActionMapping mapping, ActionForm form,
413 			HttpServletRequest request, HttpServletResponse response)
414 			throws Exception {
415 		
416 		establishRequiredState(request, form);
417 		return super.toggleTab(mapping, form, request, response);
418 	}
419 
420 }