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