View Javadoc

1   /**
2    * Copyright 2005-2013 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                 Collections.sort(flattenedNodes,new Comparator() {
175                     public int compare(Object o1, Object o2) {
176                         return ( ((RouteNode)o1).getRouteNodeName().compareTo(((RouteNode)o2).getRouteNodeName()));
177                     }
178                 });
179 				for ( Iterator<DocumentType> iter = documentType.getChildrenDocTypes().iterator(); iter.hasNext(); ) {
180 					childrenDocumentTypes.add( new DocumentTypeQuickLinksStructure( iter.next() ) );
181 				}
182                 Collections.sort(childrenDocumentTypes,new Comparator() {
183                     public int compare(Object o1, Object o2) {
184                          return ( ((DocumentTypeQuickLinksStructure)o1).documentType.getLabel().compareTo(((DocumentTypeQuickLinksStructure)o2).documentType.getLabel()));
185                     }
186                 });
187 			}
188 		}
189 
190         public List getChildrenDocumentTypes() {
191             return childrenDocumentTypes;
192         }
193         public void setChildrenDocumentTypes(List<DocumentTypeQuickLinksStructure> childrenDocumentTypes) {
194             this.childrenDocumentTypes = childrenDocumentTypes;
195         }
196         public DocumentType getDocumentType() {
197             return documentType;
198         }
199         public void setDocumentType(DocumentType documentType) {
200             this.documentType = documentType;
201         }
202         public List getFlattenedNodes() {
203             return flattenedNodes;
204         }
205         public void setFlattenedNodes(List<RouteNode> flattenedNodes) {
206             this.flattenedNodes = flattenedNodes;
207         }
208         public boolean isShouldDisplay() {
209 //			if (flattenedNodes.isEmpty()) {
210 //				for (DocumentTypeQuickLinksStructure docType : childrenDocumentTypes) {
211 //					if (docType.isShouldDisplay()) {
212 //						return true;
213 //					}
214 //				}
215 //				return false;
216 //			}
217 			return true;
218 		}
219 
220 		public List<Permission> getPermissions() {
221 			if ( permissions == null ) {
222                 Predicate p = and(
223                         equal("attributeName", "documentTypeName"),
224                         equal("active", "Y"),
225                         equal("detailCriteria",
226 						KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME+"="+getDocumentType().getName()));
227 				permissions = KimApiServiceLocator.getPermissionService().findPermissions( QueryByCriteria.Builder.fromPredicates(p)).getResults();
228 //				sqlLogger.setLevel( Level.INFO );
229 			}
230 			return permissions;
231 		}
232 		
233 		public boolean isHasRelatedPermissions() {
234 			return !getPermissions().isEmpty();
235 		}
236 		
237 		public int getRelatedPermissionCount() {
238 			return getPermissions().size();
239 		}
240     }
241 
242     public static class RouteNodeForDisplay extends RouteNode {
243     	private static final long serialVersionUID = 1L;
244 		private RouteNode baseNode;
245     	
246 		public RouteNodeForDisplay( RouteNode baseNode ) {
247 			this.baseNode = baseNode;
248 		}
249 
250 		public boolean equals(Object obj) {
251 			return this.baseNode.equals(obj);
252 		}
253 
254 		public String getActivationType() {
255 			return this.baseNode.getActivationType();
256 		}
257 
258 		public BranchPrototype getBranch() {
259 			return this.baseNode.getBranch();
260 		}
261 
262 		public List<RouteNodeConfigParam> getConfigParams() {
263 			return this.baseNode.getConfigParams();
264 		}
265 
266 		public String getContentFragment() {
267 			return this.baseNode.getContentFragment();
268 		}
269 
270 		public DocumentType getDocumentType() {
271 			return this.baseNode.getDocumentType();
272 		}
273 
274 		public String getDocumentTypeId() {
275 			return this.baseNode.getDocumentTypeId();
276 		}
277 		public Group getExceptionWorkgroup() {
278 			return this.baseNode.getExceptionWorkgroup();
279 		}
280 		public String getExceptionWorkgroupId() {
281 			return this.baseNode.getExceptionWorkgroupId();
282 		}
283 		public String getExceptionWorkgroupName() {
284 			return this.baseNode.getExceptionWorkgroupName();
285 		}
286 		public Boolean getFinalApprovalInd() {
287 			return this.baseNode.getFinalApprovalInd();
288 		}
289 		public Integer getLockVerNbr() {
290 			return this.baseNode.getLockVerNbr();
291 		}
292 		public Boolean getMandatoryRouteInd() {
293 			return this.baseNode.getMandatoryRouteInd();
294 		}
295 		public List<RouteNode> getNextNodes() {
296 			return this.baseNode.getNextNodes();
297 		}
298 		public String getNodeType() {
299 			return this.baseNode.getNodeType();
300 		}
301 		public List<RouteNode> getPreviousNodes() {
302 			return this.baseNode.getPreviousNodes();
303 		}
304 		public String getRouteMethodCode() {
305 			return this.baseNode.getRouteMethodCode();
306 		}
307 		public String getRouteMethodName() {
308 			return this.baseNode.getRouteMethodName();
309 		}
310 		public String getRouteNodeId() {
311 			return this.baseNode.getRouteNodeId();
312 		}
313 		public String getRouteNodeName() {
314 			return this.baseNode.getRouteNodeName();
315 		}
316 		public RuleTemplateBo getRuleTemplate() {
317 			return this.baseNode.getRuleTemplate();
318 		}
319 		public int hashCode() {
320 			return this.baseNode.hashCode();
321 		}
322 		public boolean isExceptionGroupDefined() {
323 			return this.baseNode.isExceptionGroupDefined();
324 		}
325 		public boolean isFlexRM() {
326 			return this.baseNode.isFlexRM();
327 		}
328 		public boolean isRoleNode() {
329 			return this.baseNode.isRoleNode();
330 		}
331 		public String toString() {
332 			return this.baseNode.toString();
333 		}
334 		
335 		private List<Responsibility> responsibilities = null;
336 		
337 		public List<Responsibility> getResponsibilities() {
338 			if ( responsibilities == null ) {
339 				QueryByCriteria.Builder builder = QueryByCriteria.Builder.create();
340                 Predicate p = and(
341                     equal("template.namespaceCode", KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE),
342                     equal("template.name", KewApiConstants.DEFAULT_RESPONSIBILITY_TEMPLATE_NAME),
343                     equal("active", "Y"),
344                     equal("attributes[documentTypeName]", getDocumentType().getName())
345                     // KULRICE-8538 -- Check the route node by looping through the results below.  If it is added
346                     // into the predicate, no rows are ever returned.
347                     // equal("attributes[routeNodeName]", getRouteNodeName())
348                 );
349 
350 
351                 builder.setPredicates(p);
352 
353                 List<Responsibility> possibleResponsibilities =
354                     KimApiServiceLocator.getResponsibilityService().findResponsibilities(builder.build()).getResults();
355 
356                 if ( !possibleResponsibilities.isEmpty() ) {
357                     for ( Responsibility resp : possibleResponsibilities ) {
358                         String routeNodeName = resp.getAttributes().get( KimConstants.AttributeConstants.ROUTE_NODE_NAME);
359                         if (StringUtils.isNotEmpty(routeNodeName) && StringUtils.equals(routeNodeName, getRouteNodeName())){
360                              responsibilities.add(resp);
361                         }
362                     }
363                 } else {
364                     responsibilities = possibleResponsibilities;
365                 }
366 			}
367 			return responsibilities;
368 		}
369 		
370 		public int getResponsibilityCount() {
371 			return getResponsibilities().size();
372 		}
373 		
374 		public boolean isHasResponsibility() {
375 			return !getResponsibilities().isEmpty();
376 		}
377     }
378     
379     private void makeLookupPathParam(ActionMapping mapping, HttpServletRequest request) {
380     	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + mapping.getModuleConfig().getPrefix();
381     	request.setAttribute("basePath", basePath);
382     }
383 
384     private String stripMethodToCall(String queryString) {
385         return queryString.replaceAll("methodToCall=addDelegationRule&", "");
386     }
387 
388     private DocumentTypeService getDocumentTypeService() {
389         return KEWServiceLocator.getDocumentTypeService();
390     }
391 
392     private RuleServiceInternal getRuleService() {
393     	return KEWServiceLocator.getRuleService();
394     }
395     
396 	public DocumentHelperService getDocumentHelperService() {
397 		if(documentHelperService == null){
398 			documentHelperService = KNSServiceLocator.getDocumentHelperService();
399 		}
400 		return documentHelperService;
401 	}
402 
403 	public MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() {
404 		if(maintenanceDocumentDictionaryService == null){
405 			maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
406 		}
407 		return maintenanceDocumentDictionaryService;
408 	}
409 
410 	@Override
411 	public ActionForward toggleTab(ActionMapping mapping, ActionForm form,
412 			HttpServletRequest request, HttpServletResponse response)
413 			throws Exception {
414 		
415 		establishRequiredState(request, form);
416 		return super.toggleTab(mapping, form, request, response);
417 	}
418 
419 }