Coverage Report - org.kuali.rice.edl.impl.components.WorkflowDocumentState
 
Classes in this File Line Coverage Branch Coverage Complexity
WorkflowDocumentState
0%
0/117
0%
0/58
7
WorkflowDocumentState$buttons
0%
0/2
N/A
7
 
 1  
 /*
 2  
  * Copyright 2006-2011 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  
 
 17  
 package org.kuali.rice.edl.impl.components;
 18  
 
 19  
 import org.apache.commons.lang.StringUtils;
 20  
 import org.apache.log4j.Logger;
 21  
 import org.kuali.rice.core.framework.services.CoreFrameworkServiceLocator;
 22  
 import org.kuali.rice.core.util.RiceConstants;
 23  
 import org.kuali.rice.core.util.xml.XmlJotter;
 24  
 import org.kuali.rice.edl.impl.EDLContext;
 25  
 import org.kuali.rice.edl.impl.EDLModelComponent;
 26  
 import org.kuali.rice.edl.impl.EDLXmlUtils;
 27  
 import org.kuali.rice.edl.impl.RequestParser;
 28  
 import org.kuali.rice.edl.impl.UserAction;
 29  
 import org.kuali.rice.edl.impl.service.EdlServiceLocator;
 30  
 import org.kuali.rice.kew.exception.WorkflowException;
 31  
 import org.kuali.rice.kew.exception.WorkflowRuntimeException;
 32  
 import org.kuali.rice.kew.service.WorkflowDocument;
 33  
 import org.kuali.rice.kew.service.WorkflowInfo;
 34  
 import org.kuali.rice.kew.util.KEWConstants;
 35  
 import org.kuali.rice.kns.util.KNSConstants;
 36  
 import org.w3c.dom.Document;
 37  
 import org.w3c.dom.Element;
 38  
 
 39  
 import javax.xml.xpath.XPath;
 40  
 import javax.xml.xpath.XPathConstants;
 41  
 import javax.xml.xpath.XPathExpressionException;
 42  
 import javax.xml.xpath.XPathFactory;
 43  
 import java.util.ArrayList;
 44  
 import java.util.Date;
 45  
 import java.util.Iterator;
 46  
 import java.util.List;
 47  
 import java.util.Map;
 48  
 
 49  
 
 50  
 /**
 51  
  * Generates document state based on the workflow document in session.
 52  
  *
 53  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 54  
  *
 55  
  */
 56  0
 public class WorkflowDocumentState implements EDLModelComponent {
 57  
 
 58  0
         private static final Logger LOG = Logger.getLogger(WorkflowDocumentState.class);
 59  
 
 60  
         // The order the enum values are listed determines the order the buttons appear on the screen
 61  0
         private enum buttons{ACKNOWLEDGE, BLANKETAPPROVE, ROUTE, SAVE, COMPLETE, APPROVE, DISAPPROVE, 
 62  0
             RETURNTOPREVIOUS, FYI, CANCEL};
 63  
         
 64  
         public void updateDOM(Document dom, Element configElement, EDLContext edlContext) {
 65  
 
 66  
                 try {
 67  0
                         Element documentState = EDLXmlUtils.getDocumentStateElement(dom);
 68  
 
 69  0
                         Element dateTime = EDLXmlUtils.getOrCreateChildElement(documentState, "dateTime", true);
 70  0
                         dateTime.appendChild(dom.createTextNode(RiceConstants.getDefaultDateAndTimeFormat().format(new Date())));
 71  
 
 72  0
                         Element definition = EDLXmlUtils.getOrCreateChildElement(documentState, "definition", true);
 73  0
                         definition.appendChild(dom.createTextNode(edlContext.getEdocLiteAssociation().getDefinition()));
 74  
 
 75  0
                         Element docType = EDLXmlUtils.getOrCreateChildElement(documentState, "docType", true);
 76  0
                         docType.appendChild(dom.createTextNode(edlContext.getEdocLiteAssociation().getEdlName()));
 77  
 
 78  0
                         Element style = EDLXmlUtils.getOrCreateChildElement(documentState, "style", true);
 79  0
                         String styleName = edlContext.getEdocLiteAssociation().getStyle();
 80  0
                         if (styleName == null) {
 81  0
                                 styleName = "Default";
 82  
                         }
 83  0
                         style.appendChild(dom.createTextNode(styleName));
 84  
 
 85  0
                         Element showAttachments = EDLXmlUtils.getOrCreateChildElement(documentState, "showAttachments", true);
 86  0
                         boolean showConstants = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean(KEWConstants.KEW_NAMESPACE, KNSConstants.DetailTypes.ALL_DETAIL_TYPE, KEWConstants.SHOW_ATTACHMENTS_IND);
 87  
 
 88  0
                         showAttachments.appendChild(dom.createTextNode(Boolean.valueOf(showConstants).toString()));
 89  
 
 90  0
                         WorkflowDocument document = (WorkflowDocument)edlContext.getRequestParser().getAttribute(RequestParser.WORKFLOW_DOCUMENT_SESSION_KEY);
 91  0
                         WorkflowInfo info = new WorkflowInfo();
 92  
 
 93  0
                         boolean documentEditable = false;
 94  0
                         if (document != null) {
 95  0
                                 List validActions = determineValidActions(document);
 96  
                                 
 97  0
                                 documentEditable = isEditable(edlContext, validActions);
 98  
         
 99  0
                                 edlContext.getTransformer().setParameter("readOnly", String.valueOf(documentEditable));
 100  0
                                 addActions(dom, documentState, validActions);
 101  0
                                 boolean isAnnotatable = isAnnotatable(validActions);
 102  0
                                 EDLXmlUtils.createTextElementOnParent(documentState, "annotatable", String.valueOf(isAnnotatable));
 103  0
                                 EDLXmlUtils.createTextElementOnParent(documentState, "docId", document.getDocumentId());
 104  0
                                 Element workflowDocumentStatus = EDLXmlUtils.getOrCreateChildElement(documentState, "workflowDocumentState", true);
 105  0
                                 EDLXmlUtils.createTextElementOnParent(workflowDocumentStatus, "status", document.getStatusDisplayValue());
 106  0
                                 EDLXmlUtils.createTextElementOnParent(workflowDocumentStatus, "createDate", RiceConstants.getDefaultDateAndTimeFormat().format(document.getDateCreated()));
 107  0
                                 String[] nodeNames = document.getPreviousNodeNames();
 108  0
                                 if (nodeNames.length > 0) {
 109  0
                                     Element previousNodes = EDLXmlUtils.getOrCreateChildElement(documentState, "previousNodes", true);
 110  
                                     // don't include LAST node (where the document is currently...don't want to return to current location)
 111  0
                                     for (int i = 0; i < nodeNames.length; i++) {
 112  0
                                         EDLXmlUtils.createTextElementOnParent(previousNodes, "node", nodeNames[i]);
 113  
                                     }
 114  
                                 }
 115  0
                                 String[] currentNodeNames = info.getCurrentNodeNames(document.getDocumentId());
 116  0
                                 for (String currentNodeName : currentNodeNames) {
 117  0
                                     EDLXmlUtils.createTextElementOnParent(documentState, "currentNodeName", currentNodeName);
 118  
                                 }
 119  
 
 120  
                         }
 121  
 
 122  0
                         Element editable = EDLXmlUtils.getOrCreateChildElement(documentState, "editable", true);
 123  0
                         editable.appendChild(dom.createTextNode(String.valueOf(documentEditable)));
 124  
 
 125  
                         // display the buttons
 126  0
                         EDLXmlUtils.createTextElementOnParent(documentState, "actionable", "true");
 127  
 
 128  0
                         List globalErrors = (List)edlContext.getRequestParser().getAttribute(RequestParser.GLOBAL_ERRORS_KEY);
 129  0
                         List globalMessages = (List)edlContext.getRequestParser().getAttribute(RequestParser.GLOBAL_MESSAGES_KEY);
 130  0
                         Map<String, String> globalFieldErrors = (Map)edlContext.getRequestParser().getAttribute(RequestParser.GLOBAL_FIELD_ERRORS_KEY);
 131  0
                         EDLXmlUtils.addErrorsAndMessagesToDocument(dom, globalErrors, globalMessages, globalFieldErrors);
 132  0
             if (LOG.isDebugEnabled()) {
 133  0
                     LOG.debug("Transforming dom " + XmlJotter.jotNode(dom, true));
 134  
             }
 135  0
                 } catch (Exception e) {
 136  0
                         throw new WorkflowRuntimeException(e);
 137  0
                 }
 138  0
         }
 139  
 
 140  
     public static List<String> determineValidActions(WorkflowDocument wfdoc) throws WorkflowException {
 141  0
         String[] flags = new String[10];
 142  0
         List<String> list = new ArrayList<String>();
 143  
         
 144  0
         if (wfdoc == null) {
 145  0
             list.add(UserAction.ACTION_CREATE);
 146  0
             return list;
 147  
         }
 148  
         
 149  0
         if (wfdoc.isAcknowledgeRequested()) {
 150  0
             flags[buttons.ACKNOWLEDGE.ordinal()] = UserAction.ACTION_ACKNOWLEDGE;
 151  
         }
 152  
         
 153  0
         if (wfdoc.isApprovalRequested()) {
 154  0
             if (wfdoc.isBlanketApproveCapable()) {
 155  0
                 flags[buttons.BLANKETAPPROVE.ordinal()] = UserAction.ACTION_BLANKETAPPROVE;
 156  
             }
 157  0
             if (!wfdoc.stateIsSaved()) {
 158  0
                 flags[buttons.APPROVE.ordinal()] = UserAction.ACTION_APPROVE;
 159  0
                 flags[buttons.DISAPPROVE.ordinal()] = UserAction.ACTION_DISAPPROVE;
 160  
             }
 161  
             
 162  
             // should invoke WorkflowDocument.saveRoutingData(...).
 163  0
             flags[buttons.SAVE.ordinal()] = UserAction.ACTION_SAVE;
 164  0
             if (wfdoc.getPreviousNodeNames().length > 0) {
 165  0
                 flags[buttons.RETURNTOPREVIOUS.ordinal()] = UserAction.ACTION_RETURN_TO_PREVIOUS;
 166  
             }
 167  
         }
 168  
         
 169  
         // this will never happen, but left code in case this gets figured out later
 170  
         // if allowed to execute save/approve and complete will both show
 171  0
         else if (wfdoc.isCompletionRequested()) {
 172  0
             flags[buttons.COMPLETE.ordinal()] = UserAction.ACTION_COMPLETE;
 173  0
             if (wfdoc.isBlanketApproveCapable()) {
 174  0
                 flags[buttons.BLANKETAPPROVE.ordinal()] = UserAction.ACTION_BLANKETAPPROVE;
 175  
             }
 176  
         }
 177  
         
 178  0
         if (wfdoc.isFYIRequested()) {
 179  0
             flags[buttons.FYI.ordinal()] = UserAction.ACTION_FYI;
 180  
         }
 181  
         
 182  0
         if (wfdoc.isRouteCapable()) {
 183  0
             flags[buttons.ROUTE.ordinal()] = UserAction.ACTION_ROUTE;
 184  0
             if (wfdoc.isBlanketApproveCapable()) {
 185  0
                 flags[buttons.BLANKETAPPROVE.ordinal()] = UserAction.ACTION_BLANKETAPPROVE;
 186  
             }
 187  
         }
 188  
         
 189  0
         if (wfdoc.isApprovalRequested() || wfdoc.isRouteCapable()) {
 190  0
             flags[buttons.SAVE.ordinal()] = UserAction.ACTION_SAVE;
 191  
         }
 192  
         
 193  0
         if (wfdoc.isCompletionRequested() || wfdoc.isRouteCapable()) {
 194  0
             flags[buttons.CANCEL.ordinal()] = UserAction.ACTION_CANCEL;
 195  
         }
 196  
 
 197  0
         for (int i = 0; i < flags.length; i++) {
 198  0
             if (flags[i] != null) {
 199  0
                 list.add(flags[i]);
 200  
             }
 201  
         }
 202  
 
 203  0
         return list;
 204  
     }
 205  
         
 206  
         public static boolean isEditable(EDLContext edlContext, List actions) {
 207  0
             boolean editable = false;
 208  0
             editable = listContainsItems(actions, UserAction.EDITABLE_ACTIONS);
 209  
             // reset editable flag to true if edoclite specifies <param name="alwaysEditable">true</param>
 210  0
             Document edlDom = EdlServiceLocator.getEDocLiteService().getDefinitionXml(edlContext.getEdocLiteAssociation());
 211  
             // use xpath to check for attribute value on Config param element.
 212  0
             XPath xpath = XPathFactory.newInstance().newXPath();
 213  0
             String xpathExpression = "//config/param[@name='alwaysEditable']"; 
 214  
             try {
 215  0
                 String match = (String) xpath.evaluate(xpathExpression, edlDom, XPathConstants.STRING);
 216  0
                 if (!StringUtils.isBlank(match) && match.equals("true")) {
 217  0
                     return true;
 218  
                 }
 219  0
             } catch (XPathExpressionException e) {
 220  0
                 throw new WorkflowRuntimeException("Unable to evaluate xpath expression " + xpathExpression, e);
 221  0
                 }
 222  
 
 223  0
             return editable;
 224  
         }
 225  
         
 226  
 
 227  
     public static void addActions(Document dom, Element documentState, List actions) {
 228  0
         Element actionsPossible = EDLXmlUtils.getOrCreateChildElement(documentState, "actionsPossible", true);
 229  0
         Iterator it = actions.iterator();
 230  0
         while (it.hasNext()) {
 231  0
             String action = it.next().toString();
 232  0
             Element actionElement = dom.createElement(action);
 233  
             // if we use string.xsl we can avoid doing this here
 234  
             // (unless for some reason we decide we want different titles)
 235  0
             if (!Character.isUpperCase(action.charAt(0))) {
 236  0
                 StringBuffer sb = new StringBuffer(action);
 237  0
                 sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
 238  0
                 action = sb.toString();
 239  
             }
 240  0
             actionElement.setAttribute("title", action);
 241  0
             actionsPossible.appendChild(actionElement);
 242  0
         }
 243  
 
 244  0
         Element annotatable = EDLXmlUtils.getOrCreateChildElement(documentState, "annotatable", true);
 245  0
         annotatable.appendChild(dom.createTextNode(String.valueOf(isAnnotatable(actions))));
 246  0
     }
 247  
 
 248  
 
 249  
 
 250  
 
 251  
     public static boolean listContainsItems(List list, Object[] items) {
 252  0
         for (int i = 0; i < items.length; i++) {
 253  0
             if (list.contains(items[i])) return true;
 254  
         }
 255  0
         return false;
 256  
     }
 257  
 
 258  
     /**
 259  
      * Determines whether to display the annotation text box
 260  
      * Currently we will show the annotation box if ANY of the possible actions are
 261  
      * annotatable.
 262  
      * But what happens if we have an un-annotatable action?
 263  
      * Hey, why don't we just make all actions annotatable.
 264  
      * @param actions list of possible actions
 265  
      * @return whether to show the annotation text box
 266  
      */
 267  
     public static boolean isAnnotatable(List actions) {
 268  0
         return listContainsItems(actions, UserAction.ANNOTATABLE_ACTIONS);
 269  
     }
 270  
 
 271  
 }