001    /**
002     * Copyright 2005-2012 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.log4j.Logger;
019    import org.apache.struts.action.ActionForm;
020    import org.apache.struts.action.ActionForward;
021    import org.apache.struts.action.ActionMapping;
022    import org.apache.struts.action.ActionMessages;
023    import org.kuali.rice.core.api.config.property.ConfigContext;
024    import org.kuali.rice.core.api.criteria.Predicate;
025    import org.kuali.rice.core.api.criteria.QueryByCriteria;
026    import org.kuali.rice.kew.doctype.bo.DocumentType;
027    import org.kuali.rice.kew.doctype.service.DocumentTypeService;
028    import org.kuali.rice.kew.engine.node.BranchPrototype;
029    import org.kuali.rice.kew.engine.node.RouteNode;
030    import org.kuali.rice.kew.engine.node.RouteNodeConfigParam;
031    import org.kuali.rice.kew.rule.RuleBaseValues;
032    import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
033    import org.kuali.rice.kew.rule.service.RuleServiceInternal;
034    import org.kuali.rice.kew.service.KEWServiceLocator;
035    import org.kuali.rice.kew.api.KewApiConstants;
036    import org.kuali.rice.kew.web.KewKualiAction;
037    import org.kuali.rice.kim.api.KimConstants;
038    import org.kuali.rice.kim.api.group.Group;
039    import org.kuali.rice.kim.api.permission.Permission;
040    import org.kuali.rice.kim.api.responsibility.Responsibility;
041    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
042    import org.kuali.rice.kns.service.DocumentHelperService;
043    import org.kuali.rice.kns.service.KNSServiceLocator;
044    import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
045    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
046    import org.kuali.rice.krad.util.GlobalVariables;
047    import org.kuali.rice.krad.util.KRADConstants;
048    
049    import javax.servlet.http.HttpServletRequest;
050    import javax.servlet.http.HttpServletResponse;
051    import java.util.ArrayList;
052    import java.util.Collections;
053    import java.util.Comparator;
054    import java.util.HashMap;
055    import java.util.Iterator;
056    import java.util.List;
057    
058    import static org.kuali.rice.core.api.criteria.PredicateFactory.and;
059    import static org.kuali.rice.core.api.criteria.PredicateFactory.equal;
060    
061    
062    /**
063     * A Struts Action for building and interacting with the Rule Quick Links.
064     *
065     * @author Kuali Rice Team (rice.collab@kuali.org)
066     */
067    public class RuleQuickLinksAction extends KewKualiAction {
068    
069            private static final Logger LOG = Logger.getLogger(RuleQuickLinksAction.class);
070            
071            private MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
072            private DocumentHelperService documentHelperService;
073    
074        public ActionForward start(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
075            makeLookupPathParam(mapping, request);
076            establishRequiredState(request, form);
077            return mapping.findForward("basic");
078        }
079    
080        @SuppressWarnings("unchecked")
081            public ActionMessages establishRequiredState(HttpServletRequest request, ActionForm form) throws Exception {
082            RuleQuickLinksForm qlForm = (RuleQuickLinksForm) form;
083            List<DocumentType> documentTypes;
084            if (qlForm.getRootDocTypeName() != null) {
085                    documentTypes = new ArrayList<DocumentType>();
086                DocumentType docType = getDocumentTypeService().findByName(qlForm.getRootDocTypeName());
087                documentTypes.add(docType);
088                request.setAttribute("renderOpened", Boolean.TRUE);
089            } else {
090                    documentTypes = getDocumentTypeService().findAllCurrentRootDocuments();
091            }
092            qlForm.setDocumentTypeQuickLinksStructures(getDocumentTypeDataStructure(documentTypes));
093            int shouldDisplayCount = 0;
094            for ( DocumentTypeQuickLinksStructure dt : qlForm.getDocumentTypeQuickLinksStructures() ) {
095                    if ( dt.isShouldDisplay() ) {
096                            shouldDisplayCount++;
097                    }
098            }
099            if ( shouldDisplayCount == 1 ) {
100                request.setAttribute("renderOpened", Boolean.TRUE);
101            }
102            String documentTypeName = getMaintenanceDocumentDictionaryService().getDocumentTypeName(DocumentType.class);
103            try {
104                if ((documentTypeName != null) && getDocumentHelperService().getDocumentAuthorizer(documentTypeName).canInitiate(documentTypeName, GlobalVariables.getUserSession().getPerson())) {
105                    qlForm.setCanInitiateDocumentTypeDocument( true );
106                }
107            } catch (Exception ex) {
108                            // just skip - and don't display links
109                    LOG.error( "Unable to check initiation permission for "+ documentTypeName, ex );
110                    }
111    
112            return null;
113        }
114    
115        public ActionForward addDelegationRule(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
116            ActionForward result = null;
117    
118            String ruleTemplateId = request.getParameter("delegationRule.ruleTemplate.id");
119            String docTypeName = request.getParameter("delegationRule.documentType.name");
120            List rules = getRuleService().search(docTypeName, null, ruleTemplateId, "", null, null, Boolean.FALSE, Boolean.TRUE, new HashMap(), null);
121            
122            if (rules.size() == 1) {
123                RuleBaseValues rule = (RuleBaseValues)rules.get(0);
124                String url = ConfigContext.getCurrentContextConfig().getKEWBaseURL() +
125                    "/DelegateRule.do?methodToCall=start" +
126                    "&parentRuleId=" + rule.getId();
127                result = new ActionForward(url, true);
128            } else {
129                    makeLookupPathParam(mapping, request);
130                    result = new ActionForward(ConfigContext.getCurrentContextConfig().getKRBaseURL() + 
131                                    "/lookup.do?methodToCall=start&"+ stripMethodToCall(request.getQueryString()), true);
132            }
133            
134            return result;
135        }
136    
137            private List getDocumentTypeDataStructure(List rootDocuments) {
138                    List documentTypeQuickLinksStructures = new ArrayList();
139                    for (Iterator iter = rootDocuments.iterator(); iter.hasNext();) {
140                            DocumentTypeQuickLinksStructure quickLinkStruct =new DocumentTypeQuickLinksStructure((DocumentType) iter.next());
141                            if (! quickLinkStruct.getFlattenedNodes().isEmpty() || ! quickLinkStruct.getChildrenDocumentTypes().isEmpty()) {
142                                    documentTypeQuickLinksStructures.add(quickLinkStruct);
143                            }
144    
145                    }
146    
147                    return documentTypeQuickLinksStructures;
148            }
149    
150    
151            /**
152         * A bean to hold a DocumentType with its flattened nodes for rendering purposes
153         * on the quick links.
154         *
155     * @author Kuali Rice Team (rice.collab@kuali.org)
156         */
157        public static class DocumentTypeQuickLinksStructure {
158            private DocumentType documentType;
159            private List<RouteNode> flattenedNodes = new ArrayList<RouteNode>();
160            private List<DocumentTypeQuickLinksStructure> childrenDocumentTypes = new ArrayList<DocumentTypeQuickLinksStructure>();
161            private List<Permission> permissions = null;
162            
163            private DocumentTypeQuickLinksStructure(DocumentType documentType) {
164                            this.documentType = documentType;
165                            if ( documentType != null ) {
166                                    List<RouteNode> tempFlattenedNodes = KEWServiceLocator.getRouteNodeService()
167                                                    .getFlattenedNodes( documentType, true );
168                                    for ( RouteNode routeNode : tempFlattenedNodes ) {
169                                            if ( routeNode.isFlexRM() || routeNode.isRoleNode() ) {
170                                                    flattenedNodes.add( new RouteNodeForDisplay( routeNode ) );
171                                            }
172                                    }
173                    Collections.sort(flattenedNodes,new Comparator() {
174                        public int compare(Object o1, Object o2) {
175                            return ( ((RouteNode)o1).getRouteNodeName().compareTo(((RouteNode)o2).getRouteNodeName()));
176                        }
177                    });
178                                    for ( Iterator<DocumentType> iter = documentType.getChildrenDocTypes().iterator(); iter.hasNext(); ) {
179                                            childrenDocumentTypes.add( new DocumentTypeQuickLinksStructure( iter.next() ) );
180                                    }
181                    Collections.sort(childrenDocumentTypes,new Comparator() {
182                        public int compare(Object o1, Object o2) {
183                             return ( ((DocumentTypeQuickLinksStructure)o1).documentType.getLabel().compareTo(((DocumentTypeQuickLinksStructure)o2).documentType.getLabel()));
184                        }
185                    });
186                            }
187                    }
188    
189            public List getChildrenDocumentTypes() {
190                return childrenDocumentTypes;
191            }
192            public void setChildrenDocumentTypes(List<DocumentTypeQuickLinksStructure> childrenDocumentTypes) {
193                this.childrenDocumentTypes = childrenDocumentTypes;
194            }
195            public DocumentType getDocumentType() {
196                return documentType;
197            }
198            public void setDocumentType(DocumentType documentType) {
199                this.documentType = documentType;
200            }
201            public List getFlattenedNodes() {
202                return flattenedNodes;
203            }
204            public void setFlattenedNodes(List<RouteNode> flattenedNodes) {
205                this.flattenedNodes = flattenedNodes;
206            }
207            public boolean isShouldDisplay() {
208    //                      if (flattenedNodes.isEmpty()) {
209    //                              for (DocumentTypeQuickLinksStructure docType : childrenDocumentTypes) {
210    //                                      if (docType.isShouldDisplay()) {
211    //                                              return true;
212    //                                      }
213    //                              }
214    //                              return false;
215    //                      }
216                            return true;
217                    }
218    
219                    public List<Permission> getPermissions() {
220                            if ( permissions == null ) {
221                    Predicate p = and(
222                            equal("attributeName", "documentTypeName"),
223                            equal("active", "Y"),
224                            equal("detailCriteria",
225                                                    KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME+"="+getDocumentType().getName()));
226                                    permissions = KimApiServiceLocator.getPermissionService().findPermissions( QueryByCriteria.Builder.fromPredicates(p)).getResults();
227    //                              sqlLogger.setLevel( Level.INFO );
228                            }
229                            return permissions;
230                    }
231                    
232                    public boolean isHasRelatedPermissions() {
233                            return !getPermissions().isEmpty();
234                    }
235                    
236                    public int getRelatedPermissionCount() {
237                            return getPermissions().size();
238                    }
239        }
240    
241        public static class RouteNodeForDisplay extends RouteNode {
242            private static final long serialVersionUID = 1L;
243                    private RouteNode baseNode;
244            
245                    public RouteNodeForDisplay( RouteNode baseNode ) {
246                            this.baseNode = baseNode;
247                    }
248    
249                    public boolean equals(Object obj) {
250                            return this.baseNode.equals(obj);
251                    }
252    
253                    public String getActivationType() {
254                            return this.baseNode.getActivationType();
255                    }
256    
257                    public BranchPrototype getBranch() {
258                            return this.baseNode.getBranch();
259                    }
260    
261                    public List<RouteNodeConfigParam> getConfigParams() {
262                            return this.baseNode.getConfigParams();
263                    }
264    
265                    public String getContentFragment() {
266                            return this.baseNode.getContentFragment();
267                    }
268    
269                    public DocumentType getDocumentType() {
270                            return this.baseNode.getDocumentType();
271                    }
272    
273                    public String getDocumentTypeId() {
274                            return this.baseNode.getDocumentTypeId();
275                    }
276                    public Group getExceptionWorkgroup() {
277                            return this.baseNode.getExceptionWorkgroup();
278                    }
279                    public String getExceptionWorkgroupId() {
280                            return this.baseNode.getExceptionWorkgroupId();
281                    }
282                    public String getExceptionWorkgroupName() {
283                            return this.baseNode.getExceptionWorkgroupName();
284                    }
285                    public Boolean getFinalApprovalInd() {
286                            return this.baseNode.getFinalApprovalInd();
287                    }
288                    public Integer getLockVerNbr() {
289                            return this.baseNode.getLockVerNbr();
290                    }
291                    public Boolean getMandatoryRouteInd() {
292                            return this.baseNode.getMandatoryRouteInd();
293                    }
294                    public List<RouteNode> getNextNodes() {
295                            return this.baseNode.getNextNodes();
296                    }
297                    public String getNodeType() {
298                            return this.baseNode.getNodeType();
299                    }
300                    public List<RouteNode> getPreviousNodes() {
301                            return this.baseNode.getPreviousNodes();
302                    }
303                    public String getRouteMethodCode() {
304                            return this.baseNode.getRouteMethodCode();
305                    }
306                    public String getRouteMethodName() {
307                            return this.baseNode.getRouteMethodName();
308                    }
309                    public String getRouteNodeId() {
310                            return this.baseNode.getRouteNodeId();
311                    }
312                    public String getRouteNodeName() {
313                            return this.baseNode.getRouteNodeName();
314                    }
315                    public RuleTemplateBo getRuleTemplate() {
316                            return this.baseNode.getRuleTemplate();
317                    }
318                    public int hashCode() {
319                            return this.baseNode.hashCode();
320                    }
321                    public boolean isExceptionGroupDefined() {
322                            return this.baseNode.isExceptionGroupDefined();
323                    }
324                    public boolean isFlexRM() {
325                            return this.baseNode.isFlexRM();
326                    }
327                    public boolean isRoleNode() {
328                            return this.baseNode.isRoleNode();
329                    }
330                    public String toString() {
331                            return this.baseNode.toString();
332                    }
333                    
334                    private List<Responsibility> responsibilities = null;
335                    
336                    public List<Responsibility> getResponsibilities() {
337                            if ( responsibilities == null ) {
338                                    QueryByCriteria.Builder builder = QueryByCriteria.Builder.create();
339                    Predicate p = and(
340                        equal("template.namespaceCode", KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE),
341                        equal("template.name", KewApiConstants.DEFAULT_RESPONSIBILITY_TEMPLATE_NAME),
342                        equal("active", "Y"),
343                        equal("attributes[documentTypeName]", getDocumentType().getName()),
344                        equal("attributes[routeNodeName]", getRouteNodeName())
345                    );
346                    builder.setPredicates(p);
347                                    responsibilities = KimApiServiceLocator.getResponsibilityService().findResponsibilities(builder.build()).getResults();
348                            }
349                            return responsibilities;
350                    }
351                    
352                    public int getResponsibilityCount() {
353                            return getResponsibilities().size();
354                    }
355                    
356                    public boolean isHasResponsibility() {
357                            return !getResponsibilities().isEmpty();
358                    }
359        }
360        
361        private void makeLookupPathParam(ActionMapping mapping, HttpServletRequest request) {
362            String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + mapping.getModuleConfig().getPrefix();
363            request.setAttribute("basePath", basePath);
364        }
365    
366        private String stripMethodToCall(String queryString) {
367            return queryString.replaceAll("methodToCall=addDelegationRule&", "");
368        }
369    
370        private DocumentTypeService getDocumentTypeService() {
371            return KEWServiceLocator.getDocumentTypeService();
372        }
373    
374        private RuleServiceInternal getRuleService() {
375            return KEWServiceLocator.getRuleService();
376        }
377        
378            public DocumentHelperService getDocumentHelperService() {
379                    if(documentHelperService == null){
380                            documentHelperService = KNSServiceLocator.getDocumentHelperService();
381                    }
382                    return documentHelperService;
383            }
384    
385            public MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() {
386                    if(maintenanceDocumentDictionaryService == null){
387                            maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
388                    }
389                    return maintenanceDocumentDictionaryService;
390            }
391    
392            @Override
393            public ActionForward toggleTab(ActionMapping mapping, ActionForm form,
394                            HttpServletRequest request, HttpServletResponse response)
395                            throws Exception {
396                    
397                    establishRequiredState(request, form);
398                    return super.toggleTab(mapping, form, request, response);
399            }
400    
401    }