001 /**
002 * Copyright 2005-2014 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 */
016 package org.kuali.rice.kew.rule.web;
017
018 import org.apache.commons.lang.StringUtils;
019 import org.apache.log4j.Logger;
020 import org.apache.struts.action.ActionForm;
021 import org.apache.struts.action.ActionForward;
022 import org.apache.struts.action.ActionMapping;
023 import org.apache.struts.action.ActionMessages;
024 import org.kuali.rice.core.api.config.property.ConfigContext;
025 import org.kuali.rice.core.api.criteria.Predicate;
026 import org.kuali.rice.core.api.criteria.QueryByCriteria;
027 import org.kuali.rice.kew.doctype.bo.DocumentType;
028 import org.kuali.rice.kew.doctype.service.DocumentTypeService;
029 import org.kuali.rice.kew.engine.node.BranchPrototype;
030 import org.kuali.rice.kew.engine.node.RouteNode;
031 import org.kuali.rice.kew.engine.node.RouteNodeConfigParam;
032 import org.kuali.rice.kew.rule.RuleBaseValues;
033 import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
034 import org.kuali.rice.kew.rule.service.RuleServiceInternal;
035 import org.kuali.rice.kew.service.KEWServiceLocator;
036 import org.kuali.rice.kew.api.KewApiConstants;
037 import org.kuali.rice.kew.web.KewKualiAction;
038 import org.kuali.rice.kim.api.KimConstants;
039 import org.kuali.rice.kim.api.group.Group;
040 import org.kuali.rice.kim.api.permission.Permission;
041 import org.kuali.rice.kim.api.responsibility.Responsibility;
042 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
043 import org.kuali.rice.kns.service.DocumentHelperService;
044 import org.kuali.rice.kns.service.KNSServiceLocator;
045 import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
046 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
047 import org.kuali.rice.krad.util.GlobalVariables;
048 import org.kuali.rice.krad.util.KRADConstants;
049
050 import javax.servlet.http.HttpServletRequest;
051 import javax.servlet.http.HttpServletResponse;
052 import java.util.ArrayList;
053 import java.util.Collections;
054 import java.util.Comparator;
055 import java.util.HashMap;
056 import java.util.Iterator;
057 import java.util.List;
058
059 import static org.kuali.rice.core.api.criteria.PredicateFactory.and;
060 import static org.kuali.rice.core.api.criteria.PredicateFactory.equal;
061
062
063 /**
064 * A Struts Action for building and interacting with the Rule Quick Links.
065 *
066 * @author Kuali Rice Team (rice.collab@kuali.org)
067 */
068 public class RuleQuickLinksAction extends KewKualiAction {
069
070 private static final Logger LOG = Logger.getLogger(RuleQuickLinksAction.class);
071
072 private MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
073 private DocumentHelperService documentHelperService;
074
075 public ActionForward start(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
076 makeLookupPathParam(mapping, request);
077 establishRequiredState(request, form);
078 return mapping.findForward("basic");
079 }
080
081 @SuppressWarnings("unchecked")
082 public ActionMessages establishRequiredState(HttpServletRequest request, ActionForm form) throws Exception {
083 RuleQuickLinksForm qlForm = (RuleQuickLinksForm) form;
084 List<DocumentType> documentTypes;
085 if (qlForm.getRootDocTypeName() != null) {
086 documentTypes = new ArrayList<DocumentType>();
087 DocumentType docType = getDocumentTypeService().findByName(qlForm.getRootDocTypeName());
088 documentTypes.add(docType);
089 request.setAttribute("renderOpened", Boolean.TRUE);
090 } else {
091 documentTypes = getDocumentTypeService().findAllCurrentRootDocuments();
092 }
093 qlForm.setDocumentTypeQuickLinksStructures(getDocumentTypeDataStructure(documentTypes));
094 int shouldDisplayCount = 0;
095 for ( DocumentTypeQuickLinksStructure dt : qlForm.getDocumentTypeQuickLinksStructures() ) {
096 if ( dt.isShouldDisplay() ) {
097 shouldDisplayCount++;
098 }
099 }
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 }