Coverage Report - org.kuali.rice.krad.web.controller.DocumentControllerBase
 
Classes in this File Line Coverage Branch Coverage Complexity
DocumentControllerBase
0%
0/94
0%
0/38
2.529
 
 1  
 /*
 2  
  * Copyright 2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 1.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/ecl1.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.krad.web.controller;
 17  
 
 18  
 import org.apache.commons.lang.ArrayUtils;
 19  
 import org.kuali.rice.core.framework.parameter.ParameterConstants;
 20  
 import org.kuali.rice.core.framework.services.CoreFrameworkServiceLocator;
 21  
 import org.kuali.rice.kew.api.WorkflowDocument;
 22  
 import org.kuali.rice.kew.exception.WorkflowException;
 23  
 import org.kuali.rice.kew.util.KEWConstants;
 24  
 import org.kuali.rice.krad.bo.AdHocRouteRecipient;
 25  
 import org.kuali.rice.krad.document.Document;
 26  
 import org.kuali.rice.krad.exception.DocumentAuthorizationException;
 27  
 import org.kuali.rice.krad.exception.UnknownDocumentIdException;
 28  
 import org.kuali.rice.krad.exception.ValidationException;
 29  
 import org.kuali.rice.krad.question.ConfirmationQuestion;
 30  
 import org.kuali.rice.krad.service.BusinessObjectService;
 31  
 import org.kuali.rice.krad.service.DataDictionaryService;
 32  
 import org.kuali.rice.krad.service.DocumentHelperService;
 33  
 import org.kuali.rice.krad.service.DocumentService;
 34  
 import org.kuali.rice.krad.service.KRADServiceLocator;
 35  
 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
 36  
 import org.kuali.rice.krad.util.GlobalVariables;
 37  
 import org.kuali.rice.krad.util.KRADConstants;
 38  
 import org.kuali.rice.krad.util.KRADPropertyConstants;
 39  
 import org.kuali.rice.krad.util.SessionTicket;
 40  
 import org.kuali.rice.krad.web.form.DocumentFormBase;
 41  
 import org.springframework.validation.BindingResult;
 42  
 import org.springframework.web.bind.annotation.ModelAttribute;
 43  
 import org.springframework.web.bind.annotation.RequestMapping;
 44  
 import org.springframework.web.servlet.ModelAndView;
 45  
 
 46  
 import javax.servlet.http.HttpServletRequest;
 47  
 import javax.servlet.http.HttpServletResponse;
 48  
 import java.util.ArrayList;
 49  
 import java.util.HashMap;
 50  
 import java.util.Map;
 51  
 
 52  
 /**
 53  
  * Base controller class for all KRAD <code>DocumentView</code> screens working
 54  
  * with <code>Document</code> models
 55  
  * 
 56  
  * <p>
 57  
  * Provides default controller implementations for the standard document actions
 58  
  * including: doc handler (retrieve from doc search and action list), save,
 59  
  * route (and other KEW actions)
 60  
  * </p>
 61  
  * 
 62  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 63  
  */
 64  0
 public abstract class DocumentControllerBase extends UifControllerBase {
 65  0
         private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentControllerBase.class);
 66  
 
 67  
         // COMMAND constants which cause docHandler to load an existing document
 68  
         // instead of creating a new one
 69  0
         protected static final String[] DOCUMENT_LOAD_COMMANDS = { KEWConstants.ACTIONLIST_COMMAND,
 70  
                         KEWConstants.DOCSEARCH_COMMAND, KEWConstants.SUPERUSER_COMMAND, KEWConstants.HELPDESK_ACTIONLIST_COMMAND };
 71  
 
 72  
         private BusinessObjectService businessObjectService;
 73  
         private DataDictionaryService dataDictionaryService;
 74  
         private DocumentService documentService;
 75  
         private DocumentHelperService documentHelperService;
 76  
 
 77  
         @Override
 78  
         public abstract DocumentFormBase createInitialForm(HttpServletRequest request);
 79  
 
 80  
         /**
 81  
          * Used to funnel all document handling through, we could do useful things
 82  
          * like log and record various openings and status Additionally it may be
 83  
          * nice to have a single dispatcher that can know how to dispatch to a
 84  
          * redirect url for document specific handling but we may not need that as
 85  
          * all we should need is the document to be able to load itself based on
 86  
          * document id and then which action forward or redirect is pertinent for
 87  
          * the document type.
 88  
          */
 89  
         @RequestMapping(params = "methodToCall=docHandler")
 90  
         public ModelAndView docHandler(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, HttpServletRequest request,
 91  
                         HttpServletResponse response) throws Exception {
 92  0
                 String command = form.getCommand();
 93  
 
 94  
                 // in all of the following cases we want to load the document
 95  0
                 if (ArrayUtils.contains(DOCUMENT_LOAD_COMMANDS, command) && form.getDocId() != null) {
 96  0
                         loadDocument(form);
 97  
                 }
 98  0
                 else if (KEWConstants.INITIATE_COMMAND.equals(command)) {
 99  0
                         createDocument(form);
 100  
                 }
 101  
                 else {
 102  0
                         LOG.error("docHandler called with invalid parameters");
 103  0
                         throw new IllegalStateException("docHandler called with invalid parameters");
 104  
                 }
 105  
 
 106  
                 // TODO: authorization on document actions
 107  
                 // if (KEWConstants.SUPERUSER_COMMAND.equalsIgnoreCase(command)) {
 108  
                 // form.setSuppressAllButtons(true);
 109  
                 // }
 110  
 
 111  0
                 return getUIFModelAndView(form);
 112  
         }
 113  
 
 114  
         /**
 115  
          * Loads the document by its provided document header id. This has been
 116  
          * abstracted out so that it can be overridden in children if the need
 117  
          * arises.
 118  
          * 
 119  
          * @param form
 120  
          *            - form instance that contains the doc id parameter and where
 121  
          *            the retrieved document instance should be set
 122  
          */
 123  
         protected void loadDocument(DocumentFormBase form) throws WorkflowException {
 124  0
                 String docId = form.getDocId();
 125  
 
 126  0
                 Document doc = null;
 127  0
                 doc = getDocumentService().getByDocumentHeaderId(docId);
 128  0
                 if (doc == null) {
 129  0
                         throw new UnknownDocumentIdException(
 130  
                                         "Document no longer exists.  It may have been cancelled before being saved.");
 131  
                 }
 132  
 
 133  0
                 WorkflowDocument workflowDocument = doc.getDocumentHeader().getWorkflowDocument();
 134  0
                 if (!getDocumentHelperService().getDocumentAuthorizer(doc).canOpen(doc,
 135  
                                 GlobalVariables.getUserSession().getPerson())) {
 136  0
                         throw buildAuthorizationException("open", doc);
 137  
                 }
 138  
 
 139  
                 // re-retrieve the document using the current user's session - remove
 140  
                 // the system user from the WorkflowDcument object
 141  0
                 if (workflowDocument != doc.getDocumentHeader().getWorkflowDocument()) {
 142  0
                         LOG.warn("Workflow document changed via canOpen check");
 143  0
                         doc.getDocumentHeader().setWorkflowDocument(workflowDocument);
 144  
                 }
 145  
 
 146  0
                 form.setDocument(doc);
 147  0
                 WorkflowDocument workflowDoc = doc.getDocumentHeader().getWorkflowDocument();
 148  0
                 form.setDocTypeName(workflowDoc.getDocumentTypeName());
 149  
 
 150  0
                 KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(), workflowDoc);
 151  0
         }
 152  
 
 153  
         /**
 154  
          * Creates a new document of the type specified by the docTypeName property
 155  
          * of the given form. This has been abstracted out so that it can be
 156  
          * overridden in children if the need arises.
 157  
          * 
 158  
          * @param form
 159  
          *            - form instance that contains the doc type parameter and where
 160  
          *            the new document instance should be set
 161  
          */
 162  
         protected void createDocument(DocumentFormBase form) throws WorkflowException {
 163  0
                 Document doc = getDocumentService().getNewDocument(form.getDocTypeName());
 164  
 
 165  0
                 form.setDocument(doc);
 166  0
                 form.setDocTypeName(doc.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
 167  0
         }
 168  
 
 169  
         /**
 170  
          * Saves the <code>Document</code> instance
 171  
          */
 172  
         @RequestMapping(params = "methodToCall=save")
 173  
         public ModelAndView save(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, HttpServletRequest request,
 174  
                         HttpServletResponse response) throws Exception {
 175  
 
 176  0
                 doProcessingAfterPost(form, request);
 177  
 
 178  
                 // get any possible changes to to adHocWorkgroups
 179  
                 // TODO turn this back on
 180  
                 // refreshAdHocRoutingWorkgroupLookups(request, form);
 181  
 
 182  0
                 Document document = form.getDocument();
 183  
 
 184  0
                 String viewName = checkAndWarnAboutSensitiveData(form, request, response,
 185  
                                 KRADPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "save", "");
 186  
                 // TODO if the happens we may need to save form to session or account
 187  
                 // for it
 188  0
                 if (viewName != null) {
 189  0
                         return new ModelAndView(viewName);
 190  
                 }
 191  
 
 192  
                 try {
 193  
                     // save in workflow
 194  0
                     getDocumentService().saveDocument(document);
 195  
 
 196  
             // TODO: should add message to message map
 197  
                     //GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_SAVED);
 198  0
                     form.setAnnotation("");
 199  
                 }
 200  0
                 catch(ValidationException vex) {
 201  
                     // if errors in map, swallow exception so screen will draw with errors
 202  
                         // if not then throw runtime because something bad happened
 203  0
                         if(GlobalVariables.getMessageMap().hasNoErrors()) {
 204  0
                                 throw new RuntimeException("Validation Exception with no error message.", vex);
 205  
                         }
 206  0
                 }
 207  
 
 208  0
                 return getUIFModelAndView(form);
 209  
         }
 210  
 
 211  
         /**
 212  
          * Routes the <code>Document</code> instance using the document service
 213  
          */
 214  
         @RequestMapping(params = "methodToCall=route")
 215  
         public ModelAndView route(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, HttpServletRequest request,
 216  
                         HttpServletResponse response) throws Exception {
 217  0
                 doProcessingAfterPost(form, request);
 218  
 
 219  
                 // TODO: prerules
 220  
                 // kualiDocumentFormBase.setDerivedValuesOnForm(request);
 221  
                 // ActionForward preRulesForward = promptBeforeValidation(mapping, form,
 222  
                 // request, response);
 223  
                 // if (preRulesForward != null) {
 224  
                 // return preRulesForward;
 225  
                 // }
 226  
 
 227  0
                 Document document = form.getDocument();
 228  
 
 229  0
                 String viewName = checkAndWarnAboutSensitiveData(form, request, response,
 230  
                                 KRADPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "route", "");
 231  0
                 if (viewName != null) {
 232  0
                         return new ModelAndView(viewName);
 233  
                 }
 234  
 
 235  
                 // TODO: adhoc recipients
 236  
                 // getDocumentService().routeDocument(document, form.getAnnotation(),
 237  
                 // combineAdHocRecipients(kualiDocumentFormBase));
 238  0
                 getDocumentService().routeDocument(document, form.getAnnotation(), new ArrayList<AdHocRouteRecipient>());
 239  
 
 240  
         // TODO: should added message to message map
 241  
                 //GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_SUCCESSFUL);
 242  0
                 form.setAnnotation("");
 243  
 
 244  
                 // GlobalVariables.getUserSession().addObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_OBJECT_KEY,Boolean.TRUE);
 245  0
                 return getUIFModelAndView(form);
 246  
         }
 247  
 
 248  
         /**
 249  
          * Does all special processing on a document that should happen on each HTTP
 250  
          * post (ie, save, route, approve, etc).
 251  
          * 
 252  
          * @param form
 253  
          * @param request
 254  
          */
 255  
         protected void doProcessingAfterPost(DocumentFormBase form, HttpServletRequest request) {
 256  0
                 getBusinessObjectService().linkUserFields(form.getDocument());
 257  0
         }
 258  
 
 259  
         // TODO this needs more analysis before porting can finish
 260  
         /*
 261  
          * protected void refreshAdHocRoutingWorkgroupLookups(HttpServletRequest
 262  
          * request, DocumentFormBase form) throws WorkflowException { for
 263  
          * (Enumeration<String> i = request.getParameterNames();
 264  
          * i.hasMoreElements();) { String parameterName = i.nextElement();
 265  
          * 
 266  
          * // TODO does this really belong in the loop if
 267  
          * (parameterName.equals("newAdHocRouteWorkgroup.recipientName") &&
 268  
          * !"".equals(request.getParameter(parameterName))) { //check for namespace
 269  
          * String namespace = KimConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE; if
 270  
          * (request.getParameter("newAdHocRouteWorkgroup.recipientNamespaceCode") !=
 271  
          * null &&
 272  
          * !"".equals(request.getParameter("newAdHocRouteWorkgroup.recipientName"
 273  
          * ).trim())) {
 274  
          * 
 275  
          * namespace =
 276  
          * request.getParameter("newAdHocRouteWorkgroup.recipientNamespaceCode"
 277  
          * ).trim(); } Group group =
 278  
          * getIdentityManagementService().getGroupByName(namespace,
 279  
          * request.getParameter(parameterName)); if (group != null) {
 280  
          * form.getNewAdHocRouteWorkgroup().setId(group.getGroupId());
 281  
          * form.getNewAdHocRouteWorkgroup().setRecipientName(group.getGroupName());
 282  
          * form
 283  
          * .getNewAdHocRouteWorkgroup().setRecipientNamespaceCode(group.getNamespaceCode
 284  
          * ()); } else { throw new
 285  
          * RuntimeException("Invalid workgroup id passed as parameter."); } }
 286  
          * 
 287  
          * // TODO need to look at screen, will most of this just be bound to the
 288  
          * form by spring? if (parameterName.startsWith("adHocRouteWorkgroup[") &&
 289  
          * !"".equals(request.getParameter(parameterName))) { if
 290  
          * (parameterName.endsWith(".recipientName")) { int lineNumber =
 291  
          * Integer.parseInt(StringUtils.substringBetween(parameterName, "[", "]"));
 292  
          * //check for namespace String namespaceParam = "adHocRouteWorkgroup[" +
 293  
          * lineNumber + "].recipientNamespaceCode"; String namespace =
 294  
          * KimConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE; if
 295  
          * (request.getParameter(namespaceParam) != null &&
 296  
          * !"".equals(request.getParameter(namespaceParam).trim())) { namespace =
 297  
          * request.getParameter(namespaceParam).trim(); } Group group =
 298  
          * getIdentityManagementService().getGroupByName(namespace,
 299  
          * request.getParameter(parameterName)); if (group != null) {
 300  
          * form.getAdHocRouteWorkgroup(lineNumber).setId(group.getGroupId());
 301  
          * form.getAdHocRouteWorkgroup
 302  
          * (lineNumber).setRecipientName(group.getGroupName());
 303  
          * form.getAdHocRouteWorkgroup
 304  
          * (lineNumber).setRecipientNamespaceCode(group.getNamespaceCode()); } else
 305  
          * { throw new
 306  
          * RuntimeException("Invalid workgroup id passed as parameter."); } } } } }
 307  
          */
 308  
 
 309  
         /**
 310  
          * Checks if the given value matches patterns that indicate sensitive data
 311  
          * and if configured to give a warning for sensitive data will prompt the
 312  
          * user to continue.
 313  
          * 
 314  
          * @param form
 315  
          * @param request
 316  
          * @param response
 317  
          * @param fieldName
 318  
          *            - name of field with value being checked
 319  
          * @param fieldValue
 320  
          *            - value to check for sensitive data
 321  
          * @param caller
 322  
          *            - method that should be called back from question
 323  
          * @param context
 324  
          *            - additional context that needs to be passed back with the
 325  
          *            question response
 326  
          * @return - view for spring to forward to, or null if processing should
 327  
          *         continue
 328  
          * @throws Exception
 329  
          */
 330  
         protected String checkAndWarnAboutSensitiveData(DocumentFormBase form, HttpServletRequest request,
 331  
                         HttpServletResponse response, String fieldName, String fieldValue, String caller, String context)
 332  
                         throws Exception {
 333  
 
 334  0
                 String viewName = null;
 335  0
                 Document document = form.getDocument();
 336  
 
 337  
         // TODO: need to move containsSensitiveDataPatternMatch to util class in krad
 338  0
         boolean containsSensitiveData = false;
 339  
                 //boolean containsSensitiveData = WebUtils.containsSensitiveDataPatternMatch(fieldValue);
 340  
 
 341  
                 // check if warning is configured in which case we will prompt, or if
 342  
                 // not business rules will thrown an error
 343  0
                 boolean warnForSensitiveData = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean(
 344  
                                 KRADConstants.KRAD_NAMESPACE, ParameterConstants.ALL_COMPONENT,
 345  
                                 KRADConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS_WARNING_IND);
 346  
 
 347  
                 // determine if the question has been asked yet
 348  0
                 Map<String, String> ticketContext = new HashMap<String, String>();
 349  0
                 ticketContext.put(KRADPropertyConstants.DOCUMENT_NUMBER, document.getDocumentNumber());
 350  0
                 ticketContext.put(KRADConstants.CALLING_METHOD, caller);
 351  0
                 ticketContext.put(KRADPropertyConstants.NAME, fieldName);
 352  
 
 353  0
                 boolean questionAsked = GlobalVariables.getUserSession().hasMatchingSessionTicket(
 354  
                                 KRADConstants.SENSITIVE_DATA_QUESTION_SESSION_TICKET, ticketContext);
 355  
 
 356  
                 // start in logic for confirming the sensitive data
 357  0
                 if (containsSensitiveData && warnForSensitiveData && !questionAsked) {
 358  0
                         Object question = request.getParameter(KRADConstants.QUESTION_INST_ATTRIBUTE_NAME);
 359  0
                         if (question == null || !KRADConstants.DOCUMENT_SENSITIVE_DATA_QUESTION.equals(question)) {
 360  
 
 361  
                                 // TODO not ready for question framework yet
 362  
                                 /*
 363  
                                  * // question hasn't been asked, prompt to continue return
 364  
                                  * this.performQuestionWithoutInput(mapping, form, request,
 365  
                                  * response, KRADConstants.DOCUMENT_SENSITIVE_DATA_QUESTION,
 366  
                                  * getKualiConfigurationService()
 367  
                                  * .getPropertyString(RiceKeyConstants
 368  
                                  * .QUESTION_SENSITIVE_DATA_DOCUMENT),
 369  
                                  * KRADConstants.CONFIRMATION_QUESTION, caller, context);
 370  
                                  */
 371  0
                                 viewName = "ask_user_questions";
 372  
                         }
 373  
                         else {
 374  0
                                 Object buttonClicked = request.getParameter(KRADConstants.QUESTION_CLICKED_BUTTON);
 375  
 
 376  
                                 // if no button clicked just reload the doc
 377  0
                                 if (ConfirmationQuestion.NO.equals(buttonClicked)) {
 378  
                                         // TODO figure out what to return
 379  0
                                         viewName = "user_says_no";
 380  
                                 }
 381  
 
 382  
                                 // answered yes, create session ticket so we not to ask question
 383  
                                 // again if there are further question requests
 384  0
                                 SessionTicket ticket = new SessionTicket(KRADConstants.SENSITIVE_DATA_QUESTION_SESSION_TICKET);
 385  0
                                 ticket.setTicketContext(ticketContext);
 386  0
                                 GlobalVariables.getUserSession().putSessionTicket(ticket);
 387  
                         }
 388  
                 }
 389  
 
 390  
                 // returning null will indicate processing should continue (no redirect)
 391  0
                 return viewName;
 392  
         }
 393  
 
 394  
         /**
 395  
          * Convenience method for building authorization exceptions
 396  
          * 
 397  
          * @param action
 398  
          *            - the action that was requested
 399  
          * @param document
 400  
          *            - document instance the action was requested for
 401  
          */
 402  
         protected DocumentAuthorizationException buildAuthorizationException(String action, Document document) {
 403  0
                 return new DocumentAuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(),
 404  
                                 action, document.getDocumentNumber());
 405  
         }
 406  
 
 407  
         public BusinessObjectService getBusinessObjectService() {
 408  0
                 if (this.businessObjectService == null) {
 409  0
                         this.businessObjectService = KRADServiceLocator.getBusinessObjectService();
 410  
                 }
 411  0
                 return this.businessObjectService;
 412  
         }
 413  
 
 414  
         public void setBusinessObjectService(BusinessObjectService businessObjectService) {
 415  0
                 this.businessObjectService = businessObjectService;
 416  0
         }
 417  
 
 418  
         public DataDictionaryService getDataDictionaryService() {
 419  0
                 if (this.dataDictionaryService == null) {
 420  0
                         this.dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
 421  
                 }
 422  0
                 return this.dataDictionaryService;
 423  
         }
 424  
 
 425  
         public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
 426  0
                 this.dataDictionaryService = dataDictionaryService;
 427  0
         }
 428  
 
 429  
         public DocumentService getDocumentService() {
 430  0
                 if (this.documentService == null) {
 431  0
                         this.documentService = KRADServiceLocatorWeb.getDocumentService();
 432  
                 }
 433  0
                 return this.documentService;
 434  
         }
 435  
 
 436  
         public void setDocumentService(DocumentService documentService) {
 437  0
                 this.documentService = documentService;
 438  0
         }
 439  
 
 440  
         public DocumentHelperService getDocumentHelperService() {
 441  0
                 if (this.documentHelperService == null) {
 442  0
                         this.documentHelperService = KRADServiceLocatorWeb.getDocumentHelperService();
 443  
                 }
 444  0
                 return this.documentHelperService;
 445  
         }
 446  
 
 447  
         public void setDocumentHelperService(DocumentHelperService documentHelperService) {
 448  0
                 this.documentHelperService = documentHelperService;
 449  0
         }
 450  
 }