View Javadoc

1   /**
2    * Copyright 2005-2012 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  package org.kuali.rice.kns.web.struts.action;
17  
18  import org.apache.commons.lang.ArrayUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.ojb.broker.OptimisticLockException;
21  import org.apache.struts.action.ActionForm;
22  import org.apache.struts.action.ActionForward;
23  import org.apache.struts.action.ActionMapping;
24  import org.apache.struts.upload.FormFile;
25  import org.kuali.rice.core.api.config.property.ConfigurationService;
26  import org.kuali.rice.core.api.util.ConcreteKeyValue;
27  import org.kuali.rice.core.api.util.KeyValue;
28  import org.kuali.rice.core.api.util.RiceConstants;
29  import org.kuali.rice.core.api.util.RiceKeyConstants;
30  import org.kuali.rice.coreservice.framework.parameter.ParameterConstants;
31  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
32  import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
33  import org.kuali.rice.kew.api.KewApiServiceLocator;
34  import org.kuali.rice.kew.api.WorkflowDocument;
35  import org.kuali.rice.kew.api.action.ActionRequest;
36  import org.kuali.rice.kew.api.action.ActionRequestType;
37  import org.kuali.rice.kew.api.action.DocumentActionParameters;
38  import org.kuali.rice.kew.api.action.WorkflowDocumentActionsService;
39  import org.kuali.rice.kew.api.doctype.DocumentType;
40  import org.kuali.rice.kew.api.exception.WorkflowException;
41  import org.kuali.rice.kew.api.KewApiConstants;
42  import org.kuali.rice.kim.api.KimConstants;
43  import org.kuali.rice.kim.api.group.Group;
44  import org.kuali.rice.kim.api.group.GroupService;
45  import org.kuali.rice.kim.api.identity.Person;
46  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
47  import org.kuali.rice.kns.datadictionary.KNSDocumentEntry;
48  import org.kuali.rice.kns.document.MaintenanceDocument;
49  import org.kuali.rice.kns.document.authorization.DocumentAuthorizer;
50  import org.kuali.rice.kns.document.authorization.DocumentAuthorizerBase;
51  import org.kuali.rice.kns.document.authorization.DocumentPresentationController;
52  import org.kuali.rice.kns.question.RecallQuestion;
53  import org.kuali.rice.kns.rule.PromptBeforeValidation;
54  import org.kuali.rice.kns.rule.event.PromptBeforeValidationEvent;
55  import org.kuali.rice.kns.service.BusinessObjectAuthorizationService;
56  import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
57  import org.kuali.rice.kns.service.DataDictionaryService;
58  import org.kuali.rice.kns.service.DocumentHelperService;
59  import org.kuali.rice.kns.service.KNSServiceLocator;
60  import org.kuali.rice.kns.util.KNSGlobalVariables;
61  import org.kuali.rice.kns.util.WebUtils;
62  import org.kuali.rice.kns.web.struts.form.BlankFormFile;
63  import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
64  import org.kuali.rice.kns.web.struts.form.KualiForm;
65  import org.kuali.rice.kns.web.struts.form.KualiMaintenanceForm;
66  import org.kuali.rice.krad.UserSession;
67  import org.kuali.rice.krad.bo.AdHocRoutePerson;
68  import org.kuali.rice.krad.bo.AdHocRouteRecipient;
69  import org.kuali.rice.krad.bo.AdHocRouteWorkgroup;
70  import org.kuali.rice.krad.bo.Attachment;
71  import org.kuali.rice.krad.bo.DocumentHeader;
72  import org.kuali.rice.krad.bo.Note;
73  import org.kuali.rice.krad.bo.PersistableBusinessObject;
74  import org.kuali.rice.krad.datadictionary.DataDictionary;
75  import org.kuali.rice.krad.document.Document;
76  import org.kuali.rice.krad.document.authorization.PessimisticLock;
77  import org.kuali.rice.krad.exception.AuthorizationException;
78  import org.kuali.rice.krad.exception.DocumentAuthorizationException;
79  import org.kuali.rice.krad.exception.UnknownDocumentIdException;
80  import org.kuali.rice.kns.question.ConfirmationQuestion;
81  import org.kuali.rice.krad.rules.rule.event.AddAdHocRoutePersonEvent;
82  import org.kuali.rice.krad.rules.rule.event.AddAdHocRouteWorkgroupEvent;
83  import org.kuali.rice.krad.rules.rule.event.AddNoteEvent;
84  import org.kuali.rice.krad.rules.rule.event.SendAdHocRequestsEvent;
85  import org.kuali.rice.krad.service.AttachmentService;
86  import org.kuali.rice.krad.service.BusinessObjectService;
87  import org.kuali.rice.krad.service.DocumentService;
88  import org.kuali.rice.krad.service.KRADServiceLocator;
89  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
90  import org.kuali.rice.krad.service.KualiRuleService;
91  import org.kuali.rice.krad.service.NoteService;
92  import org.kuali.rice.krad.service.PessimisticLockService;
93  import org.kuali.rice.krad.util.GlobalVariables;
94  import org.kuali.rice.krad.util.KRADConstants;
95  import org.kuali.rice.krad.util.KRADPropertyConstants;
96  import org.kuali.rice.krad.util.KRADUtils;
97  import org.kuali.rice.krad.util.NoteType;
98  import org.kuali.rice.krad.util.ObjectUtils;
99  import org.kuali.rice.krad.util.SessionTicket;
100 import org.kuali.rice.krad.util.UrlFactory;
101 import org.kuali.rice.ksb.api.KsbApiServiceLocator;
102 import org.springmodules.orm.ojb.OjbOperationException;
103 
104 import javax.persistence.EntityManagerFactory;
105 import javax.servlet.http.HttpServletRequest;
106 import javax.servlet.http.HttpServletResponse;
107 import javax.xml.namespace.QName;
108 
109 import java.io.ByteArrayOutputStream;
110 import java.io.IOException;
111 import java.util.ArrayList;
112 import java.util.Enumeration;
113 import java.util.HashMap;
114 import java.util.Iterator;
115 import java.util.List;
116 import java.util.Map;
117 import java.util.Properties;
118 import java.util.Set;
119 
120 
121 /**
122  * This class handles all of the document handling related actions in terms of passing them from here at a central point to the
123  * distributed transactions that actually implement document handling.
124  */
125 public class KualiDocumentActionBase extends KualiAction {
126     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiDocumentActionBase.class);
127 
128     // COMMAND constants which cause docHandler to load an existing document instead of creating a new one
129     protected static final String[] DOCUMENT_LOAD_COMMANDS = {
130             KewApiConstants.ACTIONLIST_COMMAND,
131             KewApiConstants.DOCSEARCH_COMMAND,
132             KewApiConstants.SUPERUSER_COMMAND,
133             KewApiConstants.HELPDESK_ACTIONLIST_COMMAND};
134 
135     private DataDictionaryService dataDictionaryService;
136     private DocumentHelperService documentHelperService;
137     private DocumentService documentService;
138     private ConfigurationService kualiConfigurationService;
139     private ParameterService parameterService;
140     private PessimisticLockService pessimisticLockService;
141     private KualiRuleService kualiRuleService;
142     private GroupService groupService;
143     private AttachmentService attachmentService;
144     private NoteService noteService;
145     private BusinessObjectAuthorizationService businessObjectAuthorizationService;
146     private BusinessObjectService businessObjectService;
147     private BusinessObjectMetaDataService businessObjectMetaDataService;
148     private EntityManagerFactory entityManagerFactory;
149 
150     @Override
151     protected void checkAuthorization(ActionForm form, String methodToCall) throws AuthorizationException {
152         if (!(form instanceof KualiDocumentFormBase)) {
153             super.checkAuthorization(form, methodToCall);
154         }
155     }
156 
157     /**
158      * Entry point to all actions.
159      * <p/>
160      * NOTE: No need to hook into execute for handling framwork setup anymore. Just implement the methodToCall for the framework
161      * setup, Constants.METHOD_REQUEST_PARAMETER will contain the full parameter, which can be sub stringed for getting framework
162      * parameters.
163      *
164      * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm,
165      *      javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
166      */
167     @Override
168     public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
169         ActionForward returnForward = mapping.findForward(RiceConstants.MAPPING_BASIC);
170         KualiDocumentFormBase documentForm = (KualiDocumentFormBase)form;
171 
172         // if found methodToCall, pass control to that method
173         try {
174             returnForward = super.execute(mapping, form, request, response);
175         } catch (OjbOperationException e) {
176             // special handling for OptimisticLockExceptions
177             OjbOperationException ooe = e;
178 
179             Throwable cause = ooe.getCause();
180             if (cause instanceof OptimisticLockException) {
181                 OptimisticLockException ole = (OptimisticLockException) cause;
182                 GlobalVariables.getMessageMap().putError(KRADConstants.DOCUMENT_ERRORS, RiceKeyConstants.ERROR_OPTIMISTIC_LOCK);
183                 logOjbOptimisticLockException(ole);
184             } else {
185                 // if exceptions are from 'save'
186                 throw e;
187             }
188         } finally {
189             if (form instanceof KualiDocumentFormBase) {
190                 ((KualiDocumentFormBase) form).setMessageMapFromPreviousRequest(GlobalVariables.getMessageMap());
191             }
192         }
193 
194         if (form instanceof KualiDocumentFormBase
195                 && ((KualiDocumentFormBase) form).isHasWorkflowDocument()) {
196             KualiDocumentFormBase formBase = (KualiDocumentFormBase) form;
197             Document document = formBase.getDocument();
198 
199             //KULRICE-2210 fix location of document header population
200             WorkflowDocument workflowDocument = formBase.getDocument().getDocumentHeader().getWorkflowDocument();
201             formBase.populateHeaderFields(workflowDocument);
202             formBase.setDocId(document.getDocumentNumber());
203             //End of KULRICE-2210 fix
204 
205             // check to see if document is a pessimistic lock document
206             if (isFormRepresentingLockObject(formBase)) {
207                 // form represents a document using the BO class PessimisticLock so we need to skip the authorizations in the next logic check
208                 if (LOG.isDebugEnabled()) {
209                     LOG.debug("Form " + formBase + " represents a PessimisticLock BO object");
210                 }
211             } else {
212                 // populates authorization-related fields in KualiDocumentFormBase instances, which are derived from
213                 // information which is contained in the form but which may be unavailable until this point
214                 //DocumentAuthorizer documentAuthorizer = KRADServiceLocatorInternal.getDocumentAuthorizationService().getDocumentAuthorizer(document);
215                 //formBase.populateAuthorizationFields(documentAuthorizer);
216                 populateAuthorizationFields(formBase);
217                 populateAdHocActionRequestCodes(formBase);
218 
219                 //set the formBase into userSession if the document is a session document
220                 UserSession userSession = (UserSession) request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY);
221 
222                 if (WebUtils.isDocumentSession(document, formBase)) {
223                     String formKey = formBase.getFormKey();
224                     if (StringUtils.isBlank(formBase.getFormKey()) || userSession.retrieveObject(formBase.getFormKey()) == null) {
225                         // generate doc form key here if it does not exist
226                         formKey = GlobalVariables.getUserSession().addObjectWithGeneratedKey(form);
227                         formBase.setFormKey(formKey);
228                     }
229                 }
230 
231 
232                 // below used by KualiHttpSessionListener to handle lock expiration
233                 request.getSession().setAttribute(KRADConstants.DOCUMENT_HTTP_SESSION_KEY, document.getDocumentNumber());
234                 // set returnToActionList flag, if needed
235                 if ("displayActionListView".equals(formBase.getCommand())) {
236                     formBase.setReturnToActionList(true);
237                 }
238 
239                 String attachmentEnabled =
240                         getKualiConfigurationService().getPropertyValueAsString(KRADConstants.NOTE_ATTACHMENT_ENABLED);
241                 // Override the document entry
242                 if (attachmentEnabled != null) {
243                     // This is a hack for KULRICE-1602 since the document entry is modified by a
244                     // global configuration that overrides the document templates without some sort
245                     // of rules or control
246                     //DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary();
247                     DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary();
248 
249                     org.kuali.rice.krad.datadictionary.DocumentEntry entry = (org.kuali.rice.krad.datadictionary.DocumentEntry) dataDictionary.getDocumentEntry(document.getClass().getName());
250                     entry.setAllowsNoteAttachments(Boolean.parseBoolean(attachmentEnabled));
251                 }
252                 //the request attribute will be used in KualiRequestProcess#processActionPerform
253                 if (exitingDocument()) {
254                     request.setAttribute(KRADConstants.EXITING_DOCUMENT, Boolean.TRUE);
255                 }
256 
257                 // pessimistic locking
258                 String methodCalledViaDispatch = (String) GlobalVariables.getUserSession().retrieveObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_OBJECT_KEY);
259                 if ((StringUtils.isNotBlank(methodCalledViaDispatch)) && (exitingDocument())) {
260                     GlobalVariables.getUserSession().removeObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_OBJECT_KEY);
261                     attemptLockRelease(document, methodCalledViaDispatch);
262                 }
263                 setupPessimisticLockMessages(document, request);
264                 if (!document.getPessimisticLocks().isEmpty()) {
265                     String warningMinutes = getParameterService().getParameterValueAsString(KRADConstants.KNS_NAMESPACE, KRADConstants.DetailTypes.DOCUMENT_DETAIL_TYPE, KRADConstants.SESSION_TIMEOUT_WARNING_MESSAGE_TIME_PARM_NM);
266                     request.setAttribute(KRADConstants.SESSION_TIMEOUT_WARNING_MINUTES, warningMinutes);
267                     request.setAttribute(KRADConstants.SESSION_TIMEOUT_WARNING_MILLISECONDS, (request.getSession().getMaxInactiveInterval() - (Integer.valueOf(warningMinutes) * 60)) * 1000);
268                 }
269             }
270         }
271 
272         // Pull in the pending action requests for the document and attach them to the form
273         List<ActionRequest> actionRequests = KewApiServiceLocator.getWorkflowDocumentService().getPendingActionRequests(documentForm.getDocId());
274         documentForm.setActionRequests(actionRequests);
275         
276         return returnForward;
277     }
278 
279     protected boolean isFormRepresentingLockObject(KualiDocumentFormBase form) throws Exception {
280         if (form instanceof KualiMaintenanceForm) {
281             KualiMaintenanceForm maintForm = (KualiMaintenanceForm) form;
282             if (ObjectUtils.isNotNull(maintForm.getBusinessObjectClassName())) {
283                 return PessimisticLock.class.isAssignableFrom(Class.forName(((KualiMaintenanceForm) form).getBusinessObjectClassName()));
284             }
285         }
286         return false;
287     }
288 
289     protected void attemptLockRelease(Document document, String methodToCall) {
290         if ((document != null) && (!document.getPessimisticLocks().isEmpty())) {
291             releaseLocks(document, methodToCall);
292             // refresh pessimistic locks in case custom add/remove changes were made
293             document.refreshPessimisticLocks();
294         }
295     }
296 
297     protected void releaseLocks(Document document, String methodToCall) {
298         // first check if the method to call is listed as required lock clearing
299         if (document.getLockClearningMethodNames().contains(methodToCall)) {
300             // find all locks for the current user and remove them
301             getPessimisticLockService().releaseAllLocksForUser(document.getPessimisticLocks(), GlobalVariables.getUserSession().getPerson());
302         }
303     }
304 
305     protected void setupPessimisticLockMessages(Document document, HttpServletRequest request) {
306         List<String> lockMessages = new ArrayList<String>();
307         for (PessimisticLock lock : document.getPessimisticLocks()) {
308             // if lock is owned by current user, do not display message for it
309             if (!lock.isOwnedByUser(GlobalVariables.getUserSession().getPerson())) {
310                 lockMessages.add(generatePessimisticLockMessage(lock));
311             }
312         }
313         request.setAttribute(KRADConstants.PESSIMISTIC_LOCK_MESSAGES, lockMessages);
314     }
315 
316     protected String generatePessimisticLockMessage(PessimisticLock lock) {
317         String descriptor = (lock.getLockDescriptor() != null) ? lock.getLockDescriptor() : "";
318         // TODO: this should be pulled into a properties file
319         return "This document currently has a " + descriptor + " lock owned by " + lock.getOwnedByUser().getName() + " as of " + RiceConstants.getDefaultTimeFormat().format(lock.getGeneratedTimestamp()) + " on " + RiceConstants.getDefaultDateFormat().format(lock.getGeneratedTimestamp());
320     }
321 
322 //    private void saveMessages(HttpServletRequest request) {
323 //        if (!GlobalVariables.getMessageList().isEmpty()) {
324 //            request.setAttribute(KRADConstants.GLOBAL_MESSAGES, GlobalVariables.getMessageList());
325 //        }
326 //    }
327 
328     /**
329      * This method may be used to funnel all document handling through, we could do useful things like log and record various
330      * openings and status Additionally it may be nice to have a single dispatcher that can know how to dispatch to a redirect url
331      * for document specific handling but we may not need that as all we should need is the document to be able to load itself based
332      * on document id and then which actionforward or redirect is pertinent for the document type.
333      *
334      * @param mapping
335      * @param form
336      * @param request
337      * @param response
338      * @return ActionForward
339      * @throws Exception
340      */
341     public ActionForward docHandler(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
342         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
343         String command = kualiDocumentFormBase.getCommand();
344 
345         // in all of the following cases we want to load the document
346         if (ArrayUtils.contains(DOCUMENT_LOAD_COMMANDS, command) && kualiDocumentFormBase.getDocId() != null) {
347             loadDocument(kualiDocumentFormBase);
348         } else if (KewApiConstants.INITIATE_COMMAND.equals(command)) {
349             createDocument(kualiDocumentFormBase);
350         } else {
351             LOG.error("docHandler called with invalid parameters");
352             throw new IllegalStateException("docHandler called with invalid parameters");
353         }
354 
355         // attach any extra JS from the data dictionary
356         if (LOG.isDebugEnabled()) {
357             LOG.debug("kualiDocumentFormBase.getAdditionalScriptFiles(): " + kualiDocumentFormBase.getAdditionalScriptFiles());
358         }
359         if (kualiDocumentFormBase.getAdditionalScriptFiles().isEmpty()) {
360             KNSDocumentEntry docEntry = (KNSDocumentEntry) getDataDictionaryService().getDataDictionary().getDocumentEntry(kualiDocumentFormBase.getDocument().getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
361             kualiDocumentFormBase.getAdditionalScriptFiles().addAll(docEntry.getWebScriptFiles());
362         }
363         if (KewApiConstants.SUPERUSER_COMMAND.equalsIgnoreCase(command)) {
364             kualiDocumentFormBase.setSuppressAllButtons(true);
365         }
366         return mapping.findForward(RiceConstants.MAPPING_BASIC);
367     }
368 
369     /**
370      * This method loads the document by its provided document header id. This has been abstracted out so that it can be overridden
371      * in children if the need arises.
372      *
373      * @param kualiDocumentFormBase
374      * @throws org.kuali.rice.kew.api.exception.WorkflowException
375      */
376     protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
377         String docId = kualiDocumentFormBase.getDocId();
378         Document doc = null;
379         doc = getDocumentService().getByDocumentHeaderId(docId);
380         if (doc == null) {
381             throw new UnknownDocumentIdException("Document no longer exists.  It may have been cancelled before being saved.");
382         }
383         WorkflowDocument workflowDocument = doc.getDocumentHeader().getWorkflowDocument();
384         if (!getDocumentHelperService().getDocumentAuthorizer(doc).canOpen(doc, GlobalVariables.getUserSession().getPerson())) {
385             throw buildAuthorizationException("open", doc);
386         }
387         // re-retrieve the document using the current user's session - remove the system user from the WorkflowDcument object
388         if (workflowDocument != doc.getDocumentHeader().getWorkflowDocument()) {
389             LOG.warn("Workflow document changed via canOpen check");
390             doc.getDocumentHeader().setWorkflowDocument(workflowDocument);
391         }
392         kualiDocumentFormBase.setDocument(doc);
393         WorkflowDocument workflowDoc = doc.getDocumentHeader().getWorkflowDocument();
394         kualiDocumentFormBase.setDocTypeName(workflowDoc.getDocumentTypeName());
395         // KualiDocumentFormBase.populate() needs this updated in the session
396         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(), workflowDoc);
397     }
398 
399 
400     /**
401      * This method creates a new document of the type specified by the docTypeName property of the given form. This has been
402      * abstracted out so that it can be overridden in children if the need arises.
403      *
404      * @param kualiDocumentFormBase
405      * @throws WorkflowException
406      */
407     protected void createDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
408         Document doc = getDocumentService().getNewDocument(kualiDocumentFormBase.getDocTypeName());
409 
410         kualiDocumentFormBase.setDocument(doc);
411         kualiDocumentFormBase.setDocTypeName(doc.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
412     }
413 
414     /**
415      * This method will insert the new ad hoc person from the from into the list of ad hoc person recipients, put a new new record
416      * in place and return like normal.
417      *
418      * @param mapping
419      * @param form
420      * @param request
421      * @param response
422      * @return ActionForward
423      * @throws Exception
424      */
425     public ActionForward insertAdHocRoutePerson(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
426         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
427         Document document = kualiDocumentFormBase.getDocument();
428 
429 
430         // check authorization for adding ad hoc route person
431         DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(document);
432         if (!documentAuthorizer.canSendAdHocRequests(document, kualiDocumentFormBase.getNewAdHocRoutePerson().getActionRequested(), GlobalVariables.getUserSession().getPerson())) {
433             throw buildAuthorizationException("ad-hoc route", document);
434         }
435 
436         // check business rules
437         boolean rulePassed = getKualiRuleService().applyRules(new AddAdHocRoutePersonEvent(document, kualiDocumentFormBase.getNewAdHocRoutePerson()));
438 
439         // if the rule evaluation passed, let's add the ad hoc route person
440         if (rulePassed) {
441             // uppercase userid for consistency
442 //            kualiDocumentFormBase.getNewAdHocRoutePerson().setId(StringUtils.upperCase(kualiDocumentFormBase.getNewAdHocRoutePerson().getId()));
443             kualiDocumentFormBase.getNewAdHocRoutePerson().setId(kualiDocumentFormBase.getNewAdHocRoutePerson().getId());
444             kualiDocumentFormBase.getAdHocRoutePersons().add(kualiDocumentFormBase.getNewAdHocRoutePerson());
445             AdHocRoutePerson person = new AdHocRoutePerson();
446             kualiDocumentFormBase.setNewAdHocRoutePerson(person);
447         }
448 
449         return mapping.findForward(RiceConstants.MAPPING_BASIC);
450     }
451 
452     /**
453      * This method will delete one of the ad hoc persons from the list of ad hoc persons to route to based on the line number of the
454      * delete button that was clicked. then it will return to the form.
455      *
456      * @param mapping
457      * @param form
458      * @param request
459      * @param response
460      * @return ActionForward
461      * @throws Exception
462      */
463     public ActionForward deleteAdHocRoutePerson(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
464         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
465 
466 
467         kualiDocumentFormBase.getAdHocRoutePersons().remove(this.getLineToDelete(request));
468         return mapping.findForward(RiceConstants.MAPPING_BASIC);
469     }
470 
471     /**
472      * This method will insert the new ad hoc workgroup into the list of ad hoc workgroup recipients put a nuew record in place and
473      * then return like normal.
474      *
475      * @param mapping
476      * @param form
477      * @param request
478      * @param response
479      * @return ActionForward
480      * @throws Exception
481      */
482     public ActionForward insertAdHocRouteWorkgroup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
483         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
484         Document document = kualiDocumentFormBase.getDocument();
485 
486         // check authorization for add ad hoc route workgroup
487         DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(document);
488         if (!documentAuthorizer.canSendAdHocRequests(document, kualiDocumentFormBase.getNewAdHocRouteWorkgroup().getActionRequested(), GlobalVariables.getUserSession().getPerson())) {
489             throw buildAuthorizationException("ad-hoc route", document);
490         }
491 
492         // check business rules
493         boolean rulePassed = getKualiRuleService().applyRules(new AddAdHocRouteWorkgroupEvent(document, kualiDocumentFormBase.getNewAdHocRouteWorkgroup()));
494 
495         // if the rule evaluation passed, let's add the ad hoc route workgroup
496         if (rulePassed) {
497             //fill id if not already filled
498             AdHocRouteWorkgroup newWorkgroup = kualiDocumentFormBase.getNewAdHocRouteWorkgroup();
499             if (newWorkgroup.getId() == null) {
500                 newWorkgroup.setId(KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName(
501                         newWorkgroup.getRecipientNamespaceCode(), newWorkgroup.getRecipientName()).getId());
502             }
503             kualiDocumentFormBase.getAdHocRouteWorkgroups().add(newWorkgroup);
504             AdHocRouteWorkgroup workgroup = new AdHocRouteWorkgroup();
505             kualiDocumentFormBase.setNewAdHocRouteWorkgroup(workgroup);
506         }
507 
508         return mapping.findForward(RiceConstants.MAPPING_BASIC);
509     }
510 
511     /**
512      * This method will delete one of the ad hoc workgroups from the list of ad hoc workgroups to route to based on the line number
513      * of the delete button that was clicked. then it will return
514      *
515      * @param mapping
516      * @param form
517      * @param request
518      * @param response
519      * @return ActionForward
520      * @throws Exception
521      */
522     public ActionForward deleteAdHocRouteWorkgroup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
523         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
524 
525         kualiDocumentFormBase.getAdHocRouteWorkgroups().remove(this.getLineToDelete(request));
526         return mapping.findForward(RiceConstants.MAPPING_BASIC);
527     }
528 
529     public ActionForward sendAdHocRequests(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
530         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
531         Document document = kualiDocumentFormBase.getDocument();
532 
533         boolean rulePassed = getKualiRuleService().applyRules(new SendAdHocRequestsEvent(document));
534 
535         if (rulePassed) {
536             getDocumentService().sendAdHocRequests(document, kualiDocumentFormBase.getAnnotation(), combineAdHocRecipients(kualiDocumentFormBase));
537             KNSGlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_SEND_AD_HOC_REQUESTS_SUCCESSFUL);
538         }
539 
540         return mapping.findForward(RiceConstants.MAPPING_BASIC);
541     }
542 
543     /**
544      * This method will reload the document.
545      *
546      * @param mapping
547      * @param form
548      * @param request
549      * @param response
550      * @return ActionForward
551      * @throws Exception
552      */
553     public ActionForward reload(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
554         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
555         Document document = kualiDocumentFormBase.getDocument();
556 
557         // prepare for the reload action - set doc id and command
558         kualiDocumentFormBase.setDocId(document.getDocumentNumber());
559         kualiDocumentFormBase.setCommand(DOCUMENT_LOAD_COMMANDS[1]);
560 
561         // forward off to the doc handler
562         ActionForward actionForward = docHandler(mapping, form, request, response);
563 
564         KNSGlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_RELOADED);
565         // TODO: remove this when further testing passed
566 //        if (form instanceof KualiDocumentFormBase) {
567 //            UserSession userSession = (UserSession) request.getSession().getAttribute(RiceConstants.USER_SESSION_KEY);
568 //            // force to recreate formkey in execute method
569 //            if (document instanceof SessionDocumentService && userSession.retrieveObject(kualiDocumentFormBase.getFormKey()) != null) {
570 //        	userSession.removeObject(kualiDocumentFormBase.getFormKey());;
571 //            }
572 //        }
573 
574         return actionForward;
575     }
576 
577     /**
578      * This method will save the document, which will then be available via the action list for the person who saved the document.
579      *
580      * @param mapping
581      * @param form
582      * @param request
583      * @param response
584      * @return ActionForward
585      * @throws Exception
586      */
587     public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
588         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
589         doProcessingAfterPost(kualiDocumentFormBase, request);
590         //get any possible changes to to adHocWorkgroups
591         refreshAdHocRoutingWorkgroupLookups(request, kualiDocumentFormBase);
592         Document document = kualiDocumentFormBase.getDocument();
593 
594         ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KRADPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "save", "");
595         if (forward != null) {
596             return forward;
597         }
598 
599         // save in workflow
600         getDocumentService().saveDocument(document);
601 
602         KNSGlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_SAVED);
603         kualiDocumentFormBase.setAnnotation("");
604 
605         // TODO: remove this when further testing passed
606 //        if (form instanceof KualiDocumentFormBase) {
607 //            UserSession userSession = (UserSession) request.getSession().getAttribute(RiceConstants.USER_SESSION_KEY);
608 //            // force to recreate formkey in execute method
609 //            if (document instanceof SessionDocumentService && userSession.retrieveObject(kualiDocumentFormBase.getFormKey()) != null) {
610 //        	userSession.removeObject(kualiDocumentFormBase.getFormKey());;
611 //            }
612 //        }
613 
614         return mapping.findForward(RiceConstants.MAPPING_BASIC);
615     }
616 
617     /**
618      * Checks if the given value matches patterns that indicate sensitive data and if configured to give a warning for sensitive data will
619      * prompt the user to continue
620      *
621      * @param mapping
622      * @param form
623      * @param request
624      * @param response
625      * @param fieldName  - name of field with value being checked
626      * @param fieldValue - value to check for sensitive data
627      * @param caller     - method that should be called back from question
628      * @param context    - additional context that needs to be passed back with the question response
629      * @return ActionForward which contains the question forward, or basic forward if user select no to prompt, otherwise will return null
630      *         to indicate processing should continue
631      * @throws Exception
632      */
633     protected ActionForward checkAndWarnAboutSensitiveData(ActionMapping mapping, ActionForm form,
634                                                            HttpServletRequest request, HttpServletResponse response, String fieldName, String fieldValue, String caller, String context)
635             throws Exception {
636         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
637         Document document = kualiDocumentFormBase.getDocument();
638 
639         boolean containsSensitiveData = KRADUtils.containsSensitiveDataPatternMatch(fieldValue);
640 
641         // check if warning is configured in which case we will prompt, or if not business rules will thrown an error
642         boolean warnForSensitiveData = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean(
643                 KRADConstants.KNS_NAMESPACE, ParameterConstants.ALL_COMPONENT,
644                 KRADConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS_WARNING_IND);
645 
646         // determine if the question has been asked yet
647         Map<String, String> ticketContext = new HashMap<String, String>();
648         ticketContext.put(KRADPropertyConstants.DOCUMENT_NUMBER, document.getDocumentNumber());
649         ticketContext.put(KRADConstants.CALLING_METHOD, caller);
650         ticketContext.put(KRADPropertyConstants.NAME, fieldName);
651 
652         boolean questionAsked = GlobalVariables.getUserSession().hasMatchingSessionTicket(
653                 KRADConstants.SENSITIVE_DATA_QUESTION_SESSION_TICKET, ticketContext);
654 
655         // start in logic for confirming the sensitive data
656         if (containsSensitiveData && warnForSensitiveData && !questionAsked) {
657             Object question = request.getParameter(KRADConstants.QUESTION_INST_ATTRIBUTE_NAME);
658             if (question == null || !KRADConstants.DOCUMENT_SENSITIVE_DATA_QUESTION.equals(question)) {
659 
660                 // question hasn't been asked, prompt to continue
661                 return this.performQuestionWithoutInput(mapping, form, request, response,
662                         KRADConstants.DOCUMENT_SENSITIVE_DATA_QUESTION, getKualiConfigurationService()
663                         .getPropertyValueAsString(RiceKeyConstants.QUESTION_SENSITIVE_DATA_DOCUMENT),
664                         KRADConstants.CONFIRMATION_QUESTION, caller, context);
665             }
666 
667             Object buttonClicked = request.getParameter(KRADConstants.QUESTION_CLICKED_BUTTON);
668             if (question != null && KRADConstants.DOCUMENT_SENSITIVE_DATA_QUESTION.equals(question)) {
669                 // if no button clicked just reload the doc
670                 if (ConfirmationQuestion.NO.equals(buttonClicked)) {
671 
672                     return mapping.findForward(RiceConstants.MAPPING_BASIC);
673                 }
674 
675                 // answered yes, create session ticket so we not to ask question again if there are further question requests
676                 SessionTicket ticket = new SessionTicket(KRADConstants.SENSITIVE_DATA_QUESTION_SESSION_TICKET);
677                 ticket.setTicketContext(ticketContext);
678                 GlobalVariables.getUserSession().putSessionTicket(ticket);
679             }
680         }
681 
682         // return null to indicate processing should continue (no redirect)
683         return null;
684     }
685 
686     /**
687      * This method will verify that the form is representing a {@link PessimisticLock} object and delete it if possible
688      *
689      * @param mapping
690      * @param form
691      * @param request
692      * @param response
693      * @return ActionForward
694      * @throws Exception
695      */
696     public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
697         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
698         if (isFormRepresentingLockObject(kualiDocumentFormBase)) {
699             String idValue = request.getParameter(KRADPropertyConstants.ID);
700             getPessimisticLockService().delete(idValue);
701             return returnToSender(request, mapping, kualiDocumentFormBase);
702         }
703         throw buildAuthorizationException(KRADConstants.DELETE_METHOD, kualiDocumentFormBase.getDocument());
704     }
705 
706     /**
707      * route the document using the document service
708      *
709      * @param mapping
710      * @param form
711      * @param request
712      * @param response
713      * @return ActionForward
714      * @throws Exception
715      */
716     public ActionForward performRouteReport(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
717         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
718 
719         kualiDocumentFormBase.setDerivedValuesOnForm(request);
720         ActionForward preRulesForward = promptBeforeValidation(mapping, form, request, response);
721         if (preRulesForward != null) {
722             return preRulesForward;
723         }
724 
725         Document document = kualiDocumentFormBase.getDocument();
726         // check authorization for reloading document
727         //DocumentActionFlags flags = getDocumentActionFlags(document);
728         if (!kualiDocumentFormBase.getDocumentActions().containsKey(KRADConstants.KUALI_ACTION_PERFORM_ROUTE_REPORT)) {
729             throw buildAuthorizationException("perform route report", document);
730         }
731 
732         String backUrlBase = getReturnLocation(request, mapping);
733         String globalVariableFormKey = GlobalVariables.getUserSession().addObjectWithGeneratedKey(form);
734         // setup back form variables
735         request.setAttribute("backUrlBase", backUrlBase);
736         List<KeyValue> backFormParameters = new ArrayList<KeyValue>();
737         backFormParameters.add(new ConcreteKeyValue(KRADConstants.DISPATCH_REQUEST_PARAMETER, KRADConstants.RETURN_METHOD_TO_CALL));
738         backFormParameters.add(new ConcreteKeyValue(KRADConstants.DOC_FORM_KEY, globalVariableFormKey));
739         request.setAttribute("backFormHiddenVariables", backFormParameters);
740 
741         // setup route report form variables
742         request.setAttribute("workflowRouteReportUrl", getKualiConfigurationService().getPropertyValueAsString(
743                 KRADConstants.WORKFLOW_URL_KEY) + "/" + KewApiConstants.DOCUMENT_ROUTING_REPORT_PAGE);
744         List<KeyValue> generalRouteReportFormParameters = new ArrayList<KeyValue>();
745         generalRouteReportFormParameters.add(new ConcreteKeyValue(KewApiConstants.INITIATOR_ID_ATTRIBUTE_NAME, document.getDocumentHeader().getWorkflowDocument().getDocument().getInitiatorPrincipalId()));
746         generalRouteReportFormParameters.add(new ConcreteKeyValue(KewApiConstants.DOCUMENT_TYPE_NAME_ATTRIBUTE_NAME, document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName()));
747         // prepareForRouteReport() method should populate document header workflow document application content xml
748         String xml = document.getXmlForRouteReport();
749         if (LOG.isDebugEnabled()) {
750             LOG.debug("XML being used for Routing Report is: " + xml);
751         }
752         generalRouteReportFormParameters.add(new ConcreteKeyValue(KewApiConstants.DOCUMENT_CONTENT_ATTRIBUTE_NAME, xml));
753 
754         // set up the variables for the form if java script is working (includes a close button variable and no back url)
755         List<KeyValue> javaScriptFormParameters = new ArrayList<KeyValue>();
756         javaScriptFormParameters.addAll(generalRouteReportFormParameters);
757         javaScriptFormParameters.add(new ConcreteKeyValue(KewApiConstants.DISPLAY_CLOSE_BUTTON_ATTRIBUTE_NAME, KewApiConstants.DISPLAY_CLOSE_BUTTON_TRUE_VALUE));
758         request.setAttribute("javaScriptFormVariables", javaScriptFormParameters);
759 
760         // set up the variables for the form if java script is NOT working (includes a back url but no close button)
761         List<KeyValue> noJavaScriptFormParameters = new ArrayList<KeyValue>();
762         noJavaScriptFormParameters.addAll(generalRouteReportFormParameters);
763         Properties parameters = new Properties();
764         for (KeyValue pair : backFormParameters) {
765             parameters.put(pair.getKey(), pair.getValue());
766         }
767         noJavaScriptFormParameters.add(new ConcreteKeyValue(KewApiConstants.RETURN_URL_ATTRIBUTE_NAME, UrlFactory.parameterizeUrl(backUrlBase, parameters)));
768         request.setAttribute("noJavaScriptFormVariables", noJavaScriptFormParameters);
769 
770         return mapping.findForward(KRADConstants.MAPPING_ROUTE_REPORT);
771     }
772 
773     /**
774      * route the document using the document service
775      *
776      * @param mapping
777      * @param form
778      * @param request
779      * @param response
780      * @return ActionForward
781      * @throws Exception
782      */
783     public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
784         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
785         doProcessingAfterPost(kualiDocumentFormBase, request);
786 
787         kualiDocumentFormBase.setDerivedValuesOnForm(request);
788         ActionForward preRulesForward = promptBeforeValidation(mapping, form, request, response);
789         if (preRulesForward != null) {
790             return preRulesForward;
791         }
792 
793         Document document = kualiDocumentFormBase.getDocument();
794 
795         ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KRADPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "route", "");
796         if (forward != null) {
797             return forward;
798         }
799 
800         getDocumentService().routeDocument(document, kualiDocumentFormBase.getAnnotation(), combineAdHocRecipients(kualiDocumentFormBase));
801         KNSGlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_SUCCESSFUL);
802         kualiDocumentFormBase.setAnnotation("");
803 
804 //        GlobalVariables.getUserSession().addObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_OBJECT_KEY,Boolean.TRUE);
805         return mapping.findForward(RiceConstants.MAPPING_BASIC);
806     }
807 
808     /**
809      * Calls the document service to blanket approve the document
810      *
811      * @param mapping
812      * @param form
813      * @param request
814      * @param response
815      * @return ActionForward
816      * @throws Exception
817      */
818     public ActionForward blanketApprove(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
819         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
820         doProcessingAfterPost(kualiDocumentFormBase, request);
821 
822         kualiDocumentFormBase.setDerivedValuesOnForm(request);
823         ActionForward preRulesForward = promptBeforeValidation(mapping, form, request, response);
824         if (preRulesForward != null) {
825             return preRulesForward;
826         }
827 
828         Document document = kualiDocumentFormBase.getDocument();
829 
830         ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KRADPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "blanketApprove", "");
831         if (forward != null) {
832             return forward;
833         }
834 
835         getDocumentService().blanketApproveDocument(document, kualiDocumentFormBase.getAnnotation(), combineAdHocRecipients(kualiDocumentFormBase));
836         KNSGlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_APPROVED);
837         kualiDocumentFormBase.setAnnotation("");
838         return returnToSender(request, mapping, kualiDocumentFormBase);
839     }
840 
841     /**
842      * Calls the document service to approve the document
843      *
844      * @param mapping
845      * @param form
846      * @param request
847      * @param response
848      * @return ActionForward
849      * @throws Exception
850      */
851     public ActionForward approve(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
852         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
853         doProcessingAfterPost(kualiDocumentFormBase, request);
854 
855         kualiDocumentFormBase.setDerivedValuesOnForm(request);
856         ActionForward preRulesForward = promptBeforeValidation(mapping, form, request, response);
857         if (preRulesForward != null) {
858             return preRulesForward;
859         }
860 
861         Document document = kualiDocumentFormBase.getDocument();
862 
863         ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KRADPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "approve", "");
864         if (forward != null) {
865             return forward;
866         }
867 
868         getDocumentService().approveDocument(document, kualiDocumentFormBase.getAnnotation(), combineAdHocRecipients(kualiDocumentFormBase));
869         KNSGlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_APPROVED);
870         kualiDocumentFormBase.setAnnotation("");
871         return returnToSender(request, mapping, kualiDocumentFormBase);
872     }
873 
874     /**
875      * Calls the document service to disapprove the document
876      *
877      * @param mapping
878      * @param form
879      * @param request
880      * @param response
881      * @return ActionForward
882      * @throws Exception
883      */
884     public ActionForward disapprove(ActionMapping mapping, ActionForm form, HttpServletRequest request,
885                                     HttpServletResponse response) throws Exception {
886 
887         ReasonPrompt prompt = new ReasonPrompt(KRADConstants.DOCUMENT_DISAPPROVE_QUESTION, RiceKeyConstants.QUESTION_DISAPPROVE_DOCUMENT, KRADConstants.CONFIRMATION_QUESTION, RiceKeyConstants.ERROR_DOCUMENT_DISAPPROVE_REASON_REQUIRED, KRADConstants.MAPPING_DISAPPROVE, ConfirmationQuestion.NO, RiceKeyConstants.MESSAGE_DISAPPROVAL_NOTE_TEXT_INTRO);
888         ReasonPrompt.Response resp = prompt.ask(mapping, form, request, response);
889 
890         if (resp.forward != null) {
891             return resp.forward;
892         }
893 
894         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
895         doProcessingAfterPost(kualiDocumentFormBase, request);
896         getDocumentService().disapproveDocument(kualiDocumentFormBase.getDocument(), resp.reason);
897         KNSGlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_DISAPPROVED);
898         kualiDocumentFormBase.setAnnotation("");
899 
900         return returnToSender(request, mapping, kualiDocumentFormBase);
901     }
902 
903     /**
904      * Calls the document service to cancel the document
905      *
906      * @param mapping
907      * @param form
908      * @param request
909      * @param response
910      * @return ActionForward
911      * @throws Exception
912      */
913     public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
914         Object question = request.getParameter(KRADConstants.QUESTION_INST_ATTRIBUTE_NAME);
915         // this should probably be moved into a private instance variable
916         // logic for cancel question
917         if (question == null) {
918             // ask question if not already asked
919             return this.performQuestionWithoutInput(mapping, form, request, response, KRADConstants.DOCUMENT_CANCEL_QUESTION, getKualiConfigurationService().getPropertyValueAsString(
920                     "document.question.cancel.text"), KRADConstants.CONFIRMATION_QUESTION, KRADConstants.MAPPING_CANCEL, "");
921         } else {
922             Object buttonClicked = request.getParameter(KRADConstants.QUESTION_CLICKED_BUTTON);
923             if ((KRADConstants.DOCUMENT_CANCEL_QUESTION.equals(question)) && ConfirmationQuestion.NO.equals(buttonClicked)) {
924                 // if no button clicked just reload the doc
925                 return mapping.findForward(RiceConstants.MAPPING_BASIC);
926             }
927             // else go to cancel logic below
928         }
929 
930         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
931         doProcessingAfterPost(kualiDocumentFormBase, request);
932         // KULRICE-4447 Call cancelDocument() only if the document exists
933         if (getDocumentService().documentExists(kualiDocumentFormBase.getDocId())) {
934             getDocumentService().cancelDocument(kualiDocumentFormBase.getDocument(), kualiDocumentFormBase.getAnnotation());
935         }
936 
937         return returnToSender(request, mapping, kualiDocumentFormBase);
938     }
939 
940     /**
941      * Calls the document service to disapprove the document
942      *
943      * @param mapping
944      * @param form
945      * @param request
946      * @param response
947      * @return ActionForward
948      * @throws Exception
949      */
950     public ActionForward recall(ActionMapping mapping, ActionForm form, HttpServletRequest request,
951             HttpServletResponse response) throws Exception {
952         
953         ReasonPrompt prompt = new ReasonPrompt(KRADConstants.DOCUMENT_RECALL_QUESTION, RiceKeyConstants.QUESTION_RECALL_DOCUMENT, KRADConstants.RECALL_QUESTION, RiceKeyConstants.ERROR_DOCUMENT_RECALL_REASON_REQUIRED, KRADConstants.MAPPING_RECALL, null, RiceKeyConstants.MESSAGE_RECALL_NOTE_TEXT_INTRO);
954         ReasonPrompt.Response resp = prompt.ask(mapping, form, request, response);
955 
956         if (resp.forward != null) {
957             return resp.forward;
958         }
959         
960         boolean cancel = !((KRADConstants.DOCUMENT_RECALL_QUESTION.equals(resp.question)) && RecallQuestion.RECALL_TO_ACTIONLIST.equals(resp.button));
961 
962         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
963         doProcessingAfterPost(kualiDocumentFormBase, request);
964         getDocumentService().recallDocument(kualiDocumentFormBase.getDocument(), resp.reason, cancel);
965 
966         // just return to doc view
967         return mapping.findForward(RiceConstants.MAPPING_BASIC);
968     }
969 
970     /**
971      * Close the document and take the user back to the index; only after asking the user if they want to save the document first.
972      * Only users who have the "canSave()" permission are given this option.
973      *
974      * @param mapping
975      * @param form
976      * @param request
977      * @param response
978      * @return ActionForward
979      * @throws Exception
980      */
981     public ActionForward close(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
982         KualiDocumentFormBase docForm = (KualiDocumentFormBase) form;
983         doProcessingAfterPost(docForm, request);
984         Document document = docForm.getDocument();
985         // only want to prompt them to save if they already can save
986         if (canSave(docForm)) {
987             Object question = getQuestion(request);
988             // logic for close question
989             if (question == null) {
990                 // ask question if not already asked
991                 return this.performQuestionWithoutInput(mapping, form, request, response, KRADConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION, getKualiConfigurationService().getPropertyValueAsString(
992                         RiceKeyConstants.QUESTION_SAVE_BEFORE_CLOSE), KRADConstants.CONFIRMATION_QUESTION, KRADConstants.MAPPING_CLOSE, "");
993             } else {
994                 Object buttonClicked = request.getParameter(KRADConstants.QUESTION_CLICKED_BUTTON);
995                 if ((KRADConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION.equals(question)) && ConfirmationQuestion.YES.equals(buttonClicked)) {
996                     // if yes button clicked - save the doc
997                     ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KRADPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "save", "");
998                     if (forward != null) {
999                         return forward;
1000                     }
1001 
1002                     getDocumentService().saveDocument(docForm.getDocument());
1003                 }
1004                 // else go to close logic below
1005             }
1006         }
1007 
1008         return returnToSender(request, mapping, docForm);
1009     }
1010 
1011     protected boolean canSave(ActionForm form) {
1012         KualiDocumentFormBase docForm = (KualiDocumentFormBase) form;
1013         return docForm.getDocumentActions().containsKey(KRADConstants.KUALI_ACTION_CAN_SAVE);
1014     }
1015 
1016     protected Object getQuestion(HttpServletRequest request) {
1017         return request.getParameter(KRADConstants.QUESTION_INST_ATTRIBUTE_NAME);
1018     }
1019 
1020     /**
1021      * call the document service to clear the fyis
1022      *
1023      * @param mapping
1024      * @param form
1025      * @param request
1026      * @param response
1027      * @return ActionForward
1028      * @throws Exception
1029      */
1030     public ActionForward fyi(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1031         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
1032         doProcessingAfterPost(kualiDocumentFormBase, request);
1033         getDocumentService().clearDocumentFyi(kualiDocumentFormBase.getDocument(), combineAdHocRecipients(kualiDocumentFormBase));
1034         KNSGlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_FYIED);
1035         kualiDocumentFormBase.setAnnotation("");
1036         return returnToSender(request, mapping, kualiDocumentFormBase);
1037     }
1038 
1039     /**
1040      * call the document service to acknowledge
1041      *
1042      * @param mapping
1043      * @param form
1044      * @param request
1045      * @param response
1046      * @return ActionForward
1047      * @throws Exception
1048      */
1049     public ActionForward acknowledge(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1050         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
1051         doProcessingAfterPost(kualiDocumentFormBase, request);
1052         getDocumentService().acknowledgeDocument(kualiDocumentFormBase.getDocument(), kualiDocumentFormBase.getAnnotation(), combineAdHocRecipients(kualiDocumentFormBase));
1053         KNSGlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_ACKNOWLEDGED);
1054         kualiDocumentFormBase.setAnnotation("");
1055         return returnToSender(request, mapping, kualiDocumentFormBase);
1056     }
1057 
1058     /**
1059      * redirect to the supervisor functions that exist.
1060      *
1061      * @param mapping
1062      * @param form
1063      * @param request
1064      * @param response
1065      * @return ActionForward
1066      * @throws Exception
1067      */
1068     public ActionForward supervisorFunctions(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1069         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
1070 
1071 
1072         String workflowSuperUserUrl = getKualiConfigurationService().getPropertyValueAsString(
1073                 KRADConstants.WORKFLOW_URL_KEY) + "/SuperUser.do?methodToCall=displaySuperUserDocument&documentId=" + kualiDocumentFormBase.getDocument().getDocumentHeader().getDocumentNumber();
1074         response.sendRedirect(workflowSuperUserUrl);
1075 
1076         return null;
1077     }
1078 
1079     /**
1080      * Convenience method to combine the two lists of ad hoc recipients into one which should be done before calling any of the
1081      * document service methods that expect a list of ad hoc recipients
1082      *
1083      * @param kualiDocumentFormBase
1084      * @return List
1085      */
1086     protected List<AdHocRouteRecipient> combineAdHocRecipients(KualiDocumentFormBase kualiDocumentFormBase) {
1087         List<AdHocRouteRecipient> adHocRecipients = new ArrayList<AdHocRouteRecipient>();
1088         adHocRecipients.addAll(kualiDocumentFormBase.getAdHocRoutePersons());
1089         adHocRecipients.addAll(kualiDocumentFormBase.getAdHocRouteWorkgroups());
1090         return adHocRecipients;
1091     }
1092 
1093     /**
1094      * if the action desires to retain error messages generated by the rules framework for save/submit/etc. validation after returning from a lookup.
1095      *
1096      * @see KualiAction#refresh(org.apache.struts.action.ActionMapping,
1097      *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
1098      */
1099     @Override
1100     public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1101         KualiDocumentFormBase kualiForm = (KualiDocumentFormBase) form;
1102         kualiForm.setDerivedValuesOnForm(request);
1103 
1104         super.refresh(mapping, form, request, response);
1105         refreshAdHocRoutingWorkgroupLookups(request, kualiForm);
1106 
1107         return mapping.findForward(RiceConstants.MAPPING_BASIC);
1108     }
1109 
1110     /**
1111      * special refresh needed to get the workgroups populated correctly when coming back from workgroup lookups
1112      *
1113      * @param request
1114      * @param kualiForm
1115      * @throws WorkflowException
1116      */
1117     @SuppressWarnings("unchecked")
1118     protected void refreshAdHocRoutingWorkgroupLookups(HttpServletRequest request, KualiDocumentFormBase kualiForm) throws WorkflowException {
1119         for (Enumeration<String> i = request.getParameterNames(); i.hasMoreElements();) {
1120             String parameterName = i.nextElement();
1121             if (parameterName.equals("newAdHocRouteWorkgroup.recipientName") && !"".equals(request.getParameter(parameterName))) {
1122                 //check for namespace
1123                 String namespace = KimConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE;
1124                 if (request.getParameter("newAdHocRouteWorkgroup.recipientNamespaceCode") != null && !"".equals(request.getParameter("newAdHocRouteWorkgroup.recipientName").trim())) {
1125                     namespace = request.getParameter("newAdHocRouteWorkgroup.recipientNamespaceCode").trim();
1126                 }
1127                 Group group = getGroupService().getGroupByNamespaceCodeAndName(namespace, request.getParameter(
1128                         parameterName));
1129                 if (group != null) {
1130                     kualiForm.getNewAdHocRouteWorkgroup().setId(group.getId());
1131                     kualiForm.getNewAdHocRouteWorkgroup().setRecipientName(group.getName());
1132                     kualiForm.getNewAdHocRouteWorkgroup().setRecipientNamespaceCode(group.getNamespaceCode());
1133                 } else {
1134                     throw new RuntimeException("Invalid workgroup id passed as parameter.");
1135                 }
1136             }
1137             if (parameterName.startsWith("adHocRouteWorkgroup[") && !"".equals(request.getParameter(parameterName))) {
1138                 if (parameterName.endsWith(".recipientName")) {
1139                     int lineNumber = Integer.parseInt(StringUtils.substringBetween(parameterName, "[", "]"));
1140                     //check for namespace
1141                     String namespaceParam = "adHocRouteWorkgroup[" + lineNumber + "].recipientNamespaceCode";
1142                     String namespace = KimConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE;
1143                     if (request.getParameter(namespaceParam) != null && !"".equals(request.getParameter(namespaceParam).trim())) {
1144                         namespace = request.getParameter(namespaceParam).trim();
1145                     }
1146                     Group group = getGroupService().getGroupByNamespaceCodeAndName(namespace, request.getParameter(
1147                             parameterName));
1148                     if (group != null) {
1149                         kualiForm.getAdHocRouteWorkgroup(lineNumber).setId(group.getId());
1150                         kualiForm.getAdHocRouteWorkgroup(lineNumber).setRecipientName(group.getName());
1151                         kualiForm.getAdHocRouteWorkgroup(lineNumber).setRecipientNamespaceCode(group.getNamespaceCode());
1152                     } else {
1153                         throw new RuntimeException("Invalid workgroup id passed as parameter.");
1154                     }
1155                 }
1156             }
1157             /*
1158             if (parameterName.startsWith("newAdHocRouteWorkgroup[") && !"".equals(request.getParameter(parameterName))) {
1159                 if (parameterName.endsWith(".recipientName")) {
1160                     int lineNumber = Integer.parseInt(StringUtils.substringBetween(parameterName, "[", "]"));
1161                   //check for namespace
1162                     String namespaceParam = "newAdHocRouteWorkgroup[" + lineNumber + "].recipientNamespaceCode";
1163                     String namespace = KimApiConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE;
1164                     if (request.getParameter(namespaceParam) != null && !"".equals(request.getParameter(namespaceParam).trim())) {
1165                         namespace = request.getParameter(namespaceParam).trim();
1166                     }
1167                     KimGroup group = getIdentityManagementService().getGroupByNamespaceCodeAndName(namespace, request.getParameter(parameterName));
1168                     if (group != null) {
1169                         kualiForm.getAdHocRouteWorkgroup(lineNumber).setId(group.getGroupId());
1170                         kualiForm.getAdHocRouteWorkgroup(lineNumber).setRecipientName(group.getGroupName());
1171                         kualiForm.getAdHocRouteWorkgroup(lineNumber).setRecipientNamespaceCode(group.getNamespaceCode());
1172                     } else {
1173                         throw new RuntimeException("Invalid workgroup id passed as parameter.");
1174                     }
1175                 }
1176             }
1177             */
1178         }
1179     }
1180 
1181 
1182     /**
1183      * Cancels the pending attachment, if any.
1184      *
1185      * @param mapping
1186      * @param form
1187      * @param request
1188      * @param response
1189      * @return ActionForward
1190      * @throws Exception
1191      */
1192     public ActionForward cancelBOAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1193         KualiDocumentFormBase documentForm = (KualiDocumentFormBase) form;
1194 
1195         // blank current attachmentFile
1196         documentForm.setAttachmentFile(new BlankFormFile());
1197 
1198         // remove current attachment, if any
1199         Note note = documentForm.getNewNote();
1200         note.removeAttachment();
1201         documentForm.setNewNote(note);
1202 
1203         return mapping.findForward(RiceConstants.MAPPING_BASIC);
1204     }
1205 
1206     /**
1207      * Handy method to stream the byte array to response object
1208      *
1209      * @param fileContents
1210      * @param fileName
1211      * @param fileContentType
1212      * @param response
1213      * @throws Exception
1214      */
1215     protected void streamToResponse(byte[] fileContents, String fileName, String fileContentType, HttpServletResponse response) throws Exception {
1216         ByteArrayOutputStream baos = null;
1217         try {
1218             baos = new ByteArrayOutputStream(fileContents.length);
1219             baos.write(fileContents);
1220             WebUtils.saveMimeOutputStreamAsFile(response, fileContentType, baos, fileName);
1221         } finally {
1222             try {
1223                 if (baos != null) {
1224                     baos.close();
1225                     baos = null;
1226                 }
1227             } catch (IOException ioEx) {
1228                 LOG.error("Error while downloading attachment");
1229                 throw new RuntimeException("IOException occurred while downloading attachment", ioEx);
1230             }
1231         }
1232     }
1233 
1234     /**
1235      * Downloads the selected attachment to the user's browser
1236      *
1237      * @param mapping
1238      * @param form
1239      * @param request
1240      * @param response
1241      * @return ActionForward
1242      * @throws Exception
1243      */
1244     public ActionForward downloadBOAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1245         KualiDocumentFormBase documentForm = (KualiDocumentFormBase) form;
1246 
1247         int attachmentIndex = selectedAttachmentIndex(request);
1248         if (attachmentIndex >= 0) {
1249             Note note = documentForm.getDocument().getNote(attachmentIndex);
1250             Attachment attachment = note.getAttachment();
1251             //make sure attachment is setup with backwards reference to note (rather then doing this we could also just call the attachment service (with a new method that took in the note)
1252             attachment.setNote(note);
1253 
1254             // since we're downloading a file, all of the editable properties from the previous request will continue to be editable.
1255             documentForm.copyPopulateEditablePropertiesToActionEditableProperties();
1256 
1257             WebUtils.saveMimeInputStreamAsFile(response, attachment.getAttachmentMimeTypeCode(), attachment.getAttachmentContents(), attachment.getAttachmentFileName(), attachment.getAttachmentFileSize().intValue());
1258             return null;
1259         }
1260 
1261         return mapping.findForward(RiceConstants.MAPPING_BASIC);
1262     }
1263 
1264 
1265     /**
1266      * @param request
1267      * @return index of the attachment whose download button was just pressed
1268      */
1269     protected int selectedAttachmentIndex(HttpServletRequest request) {
1270         int attachmentIndex = -1;
1271 
1272         String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
1273         if (StringUtils.isNotBlank(parameterName)) {
1274             String attachmentIndexParam = StringUtils.substringBetween(parameterName, ".attachment[", "].");
1275 
1276             try {
1277                 attachmentIndex = Integer.parseInt(attachmentIndexParam);
1278             } catch (NumberFormatException ignored) {
1279             }
1280         }
1281 
1282         return attachmentIndex;
1283     }
1284 
1285 
1286     /**
1287      * insert a note into the document
1288      *
1289      * @param mapping
1290      * @param form
1291      * @param request
1292      * @param response
1293      * @return ActionForward
1294      * @throws Exception
1295      */
1296     public ActionForward insertBONote(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1297         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
1298         Document document = kualiDocumentFormBase.getDocument();
1299         Note newNote = kualiDocumentFormBase.getNewNote();
1300         newNote.setNotePostedTimestampToCurrent();
1301 
1302         String attachmentTypeCode = null;
1303 
1304         FormFile attachmentFile = kualiDocumentFormBase.getAttachmentFile();
1305         if (attachmentFile == null) {
1306             GlobalVariables.getMessageMap().putError(
1307                     String.format("%s.%s",
1308                             KRADConstants.NEW_DOCUMENT_NOTE_PROPERTY_NAME,
1309                             KRADConstants.NOTE_ATTACHMENT_FILE_PROPERTY_NAME),
1310                     RiceKeyConstants.ERROR_UPLOADFILE_NULL);
1311             // This line was removed in order to continue to validates other
1312             // return mapping.findForward(RiceConstants.MAPPING_BASIC);
1313         }
1314 
1315         if (newNote.getAttachment() != null) {
1316             attachmentTypeCode = newNote.getAttachment().getAttachmentTypeCode();
1317         }
1318 
1319         // check authorization for adding notes
1320         DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(document);
1321         if (!documentAuthorizer.canAddNoteAttachment(document, attachmentTypeCode, GlobalVariables.getUserSession().getPerson())) {
1322             throw buildAuthorizationException("annotate", document);
1323         }
1324 
1325         // create the attachment first, so that failure-to-create-attachment can be treated as a validation failure
1326 
1327         Attachment attachment = null;
1328         if (attachmentFile != null && !StringUtils.isBlank(attachmentFile.getFileName())) {
1329             if (attachmentFile.getFileSize() == 0) {
1330                 GlobalVariables.getMessageMap().putError(
1331                         String.format("%s.%s",
1332                                 KRADConstants.NEW_DOCUMENT_NOTE_PROPERTY_NAME,
1333                                 KRADConstants.NOTE_ATTACHMENT_FILE_PROPERTY_NAME),
1334                         RiceKeyConstants.ERROR_UPLOADFILE_EMPTY,
1335                         attachmentFile.getFileName());
1336                 // This line was removed in order to continue to validates other
1337 //                return mapping.findForward(RiceConstants.MAPPING_BASIC);
1338             } else {
1339                 String attachmentType = null;
1340                 Attachment newAttachment = kualiDocumentFormBase.getNewNote().getAttachment();
1341                 if (newAttachment != null) {
1342                     attachmentType = newAttachment.getAttachmentTypeCode();
1343                 }
1344                 attachment = getAttachmentService().createAttachment(document.getNoteTarget(), attachmentFile.getFileName(), attachmentFile.getContentType(), attachmentFile.getFileSize(), attachmentFile.getInputStream(), attachmentType);
1345             }
1346         }
1347 
1348         DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary();
1349         org.kuali.rice.krad.datadictionary.DocumentEntry entry = dataDictionary.getDocumentEntry(document.getClass().getName());
1350 
1351         if (entry.getDisplayTopicFieldInNotes()) {
1352             String topicText = kualiDocumentFormBase.getNewNote().getNoteTopicText();
1353             if (StringUtils.isBlank(topicText)) {
1354                 GlobalVariables.getMessageMap().putError(
1355                         String.format("%s.%s",
1356                                 KRADConstants.NEW_DOCUMENT_NOTE_PROPERTY_NAME,
1357                                 KRADConstants.NOTE_TOPIC_TEXT_PROPERTY_NAME),
1358                         RiceKeyConstants.ERROR_REQUIRED,
1359                         "Note Topic (Note Topic)");
1360             }
1361         }
1362 
1363         // create a new note from the data passed in
1364         // TODO gah! this is awful
1365         Person kualiUser = GlobalVariables.getUserSession().getPerson();
1366         if (kualiUser == null) {
1367             throw new IllegalStateException("Current UserSession has a null Person.");
1368         }
1369         Note tmpNote = getNoteService().createNote(newNote, document.getNoteTarget(), kualiUser.getPrincipalId());
1370 
1371         ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KRADPropertyConstants.NOTE, tmpNote.getNoteText(), "insertBONote", "");
1372         if (forward != null) {
1373             return forward;
1374         }
1375 
1376         // validate the note
1377         boolean rulePassed = getKualiRuleService().applyRules(new AddNoteEvent(document, tmpNote));
1378 
1379         // if the rule evaluation passed, let's add the note
1380         if (rulePassed) {
1381             tmpNote.refresh();
1382 
1383 
1384             DocumentHeader documentHeader = document.getDocumentHeader();
1385 
1386             // associate note with object now
1387             document.addNote(tmpNote);
1388 
1389             // persist the note if the document is already saved the getObjectId check is to get around a bug with certain documents where
1390             // "saved" doesn't really persist, if you notice any problems with missing notes check this line
1391             //maintenance document BO note should only be saved into table when document is in the PROCESSED workflow status
1392             if (!documentHeader.getWorkflowDocument().isInitiated() && StringUtils.isNotEmpty(document.getNoteTarget().getObjectId())
1393                     && !(document instanceof MaintenanceDocument && NoteType.BUSINESS_OBJECT.getCode().equals(tmpNote.getNoteTypeCode()))
1394                     ) {
1395                 getNoteService().save(tmpNote);
1396             }
1397             // adding the attachment after refresh gets called, since the attachment record doesn't get persisted
1398             // until the note does (and therefore refresh doesn't have any attachment to autoload based on the id, nor does it
1399             // autopopulate the id since the note hasn't been persisted yet)
1400             if (attachment != null) {
1401                 tmpNote.addAttachment(attachment);
1402                 // save again for attachment, note this is because sometimes the attachment is added first to the above then ojb tries to save
1403                 //without the PK on the attachment I think it is safer then trying to get the sequence manually
1404                 if (!documentHeader.getWorkflowDocument().isInitiated() && StringUtils.isNotEmpty(document.getNoteTarget().getObjectId())
1405                         && !(document instanceof MaintenanceDocument && NoteType.BUSINESS_OBJECT.getCode().equals(tmpNote.getNoteTypeCode()))
1406                         ) {
1407                     getNoteService().save(tmpNote);
1408                 }
1409             }
1410 
1411 
1412             // reset the new note back to an empty one
1413             kualiDocumentFormBase.setNewNote(new Note());
1414         }
1415 
1416 
1417         return mapping.findForward(RiceConstants.MAPPING_BASIC);
1418     }
1419 
1420     /**
1421      * delete a note from the document
1422      *
1423      * @param mapping
1424      * @param form
1425      * @param request
1426      * @param response
1427      * @return ActionForward
1428      * @throws Exception
1429      */
1430     public ActionForward deleteBONote(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1431         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
1432         Document document = kualiDocumentFormBase.getDocument();
1433 
1434 
1435 //        DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary();
1436 //        DocumentEntry entry = dataDictionary.getDocumentEntry(document.getClass().getName());
1437 
1438         // check authorization for adding notes
1439         //DocumentActionFlags flags = getDocumentActionFlags(document);
1440         //if (!kualiDocumentFormBase.getDocumentActions().containsKey(KRADConstants.KUALI_ACTION_CAN_ANNOTATE)) {
1441         //    buildAuthorizationException("annotate", document);
1442         //    return mapping.findForward(RiceConstants.MAPPING_BASIC);
1443         //}
1444 
1445         // ok to delete the note/attachment
1446         // derive the note property from the newNote on the form
1447         Note newNote = kualiDocumentFormBase.getNewNote();
1448         Note note = document.getNote(getLineToDelete(request));
1449         Attachment attachment = note.getAttachment();
1450         String attachmentTypeCode = null;
1451         if (attachment != null) {
1452             attachmentTypeCode = attachment.getAttachmentTypeCode();
1453         }
1454         String authorUniversalIdentifier = note.getAuthorUniversalIdentifier();
1455         if (!WebUtils.canDeleteNoteAttachment(document, attachmentTypeCode, authorUniversalIdentifier)) {
1456             throw buildAuthorizationException("annotate", document);
1457         }
1458 
1459         if (attachment != null) { // only do this if the note has been persisted
1460             //KFSMI-798 - refresh() changed to refreshNonUpdateableReferences()
1461             //All references for the business object Attachment are auto-update="none",
1462             //so refreshNonUpdateableReferences() should work the same as refresh()
1463             if (note.getNoteIdentifier() != null) { // KULRICE-2343 don't blow away note reference if the note wasn't persisted
1464                 attachment.refreshNonUpdateableReferences();
1465             }
1466             getAttachmentService().deleteAttachmentContents(attachment);
1467         }
1468         // delete the note if the document is already saved
1469         if (!document.getDocumentHeader().getWorkflowDocument().isInitiated()) {
1470             getNoteService().deleteNote(note);
1471         }
1472         document.removeNote(note);
1473 
1474         return mapping.findForward(RiceConstants.MAPPING_BASIC);
1475     }
1476 
1477     /**
1478      * Override this to customize which routing action to take when sending a note.  This method reads the system parameter
1479      * KR-NS/Document/SEND_NOTE_WORKFLOW_NOTIFICATION_ACTIONS to determine which action to take
1480      *
1481      * @param request
1482      * @param note
1483      * @return a value from {@link KewApiConstants}
1484      */
1485     protected String determineNoteWorkflowNotificationAction(HttpServletRequest request, KualiDocumentFormBase kualiDocumentFormBase, Note note) {
1486         return getParameterService().getParameterValueAsString(KRADConstants.KNS_NAMESPACE, KRADConstants.DetailTypes.DOCUMENT_DETAIL_TYPE, KRADConstants.SEND_NOTE_WORKFLOW_NOTIFICATION_ACTIONS_PARM_NM);
1487     }
1488 
1489     public ActionForward sendNoteWorkflowNotification(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1490         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
1491         Document document = kualiDocumentFormBase.getDocument();
1492 
1493         Note note = document.getNote(getSelectedLine(request));
1494 
1495         // verify recipient was specified
1496         if (StringUtils.isBlank(note.getAdHocRouteRecipient().getId())) {
1497             GlobalVariables.getMessageMap().putError(KRADPropertyConstants.NEW_DOCUMENT_NOTE, RiceKeyConstants.ERROR_SEND_NOTE_NOTIFICATION_RECIPIENT);
1498             return mapping.findForward(RiceConstants.MAPPING_BASIC);
1499         }
1500         // check recipient is valid
1501         else {
1502             note.getAdHocRouteRecipient().setActionRequested(determineNoteWorkflowNotificationAction(request, kualiDocumentFormBase, note));
1503 
1504             boolean rulePassed = getKualiRuleService().applyRules(new AddAdHocRoutePersonEvent(KRADPropertyConstants.NEW_DOCUMENT_NOTE, document, (AdHocRoutePerson) note.getAdHocRouteRecipient()));
1505             if (!rulePassed) {
1506                 return mapping.findForward(RiceConstants.MAPPING_BASIC);
1507             }
1508         }
1509 
1510         // if document is saved, send notification
1511         if (!document.getDocumentHeader().getWorkflowDocument().isInitiated()) {
1512             getDocumentService().sendNoteRouteNotification(document, note, GlobalVariables.getUserSession().getPerson());
1513 
1514             // add success message
1515             KNSGlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_SEND_NOTE_NOTIFICATION_SUCCESSFUL);
1516         } else {
1517             GlobalVariables.getMessageMap().putError(KRADPropertyConstants.NEW_DOCUMENT_NOTE, RiceKeyConstants.ERROR_SEND_NOTE_NOTIFICATION_DOCSTATUS);
1518         }
1519 
1520         return mapping.findForward(RiceConstants.MAPPING_BASIC);
1521     }
1522 
1523 
1524     /**
1525      * Generates detailed log messages for OptimisticLockExceptions
1526      *
1527      * @param e
1528      */
1529     private final void logOjbOptimisticLockException(OptimisticLockException e) {
1530         if (LOG.isInfoEnabled()) {
1531             StringBuffer message = new StringBuffer("caught OptimisticLockException, caused by ");
1532             Object sourceObject = e.getSourceObject();
1533             String infix = null;
1534             try {
1535                 // try to add instance details
1536                 infix = sourceObject.toString();
1537             } catch (Exception e2) {
1538                 // just use the class name
1539                 infix = sourceObject.getClass().getName();
1540             }
1541             message.append(infix);
1542 
1543             if (sourceObject instanceof PersistableBusinessObject) {
1544                 PersistableBusinessObject persistableObject = (PersistableBusinessObject) sourceObject;
1545                 message.append(" [versionNumber = ").append(persistableObject.getVersionNumber()).append("]");
1546             }
1547 
1548             LOG.info(message.toString(), e);
1549         }
1550     }
1551 
1552 
1553     /**
1554      * Makes calls to the PromptBeforeValidation specified for the document. If the class returns an actionforward, that forward
1555      * will be returned (thus controlling how execution occurs), or null.
1556      *
1557      * @param mapping
1558      * @param form
1559      * @param request
1560      * @param response
1561      * @return
1562      * @throws Exception
1563      */
1564     public ActionForward promptBeforeValidation(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1565         return promptBeforeValidation(mapping, form, request, response, "route");
1566     }
1567 
1568     /**
1569      * Makes calls to the PromptBeforeValidation specified for the document. If the class returns an actionforward, that forward
1570      * will be returned (thus controlling how execution occurs), or null.
1571      *
1572      * @param mapping
1573      * @param form
1574      * @param request
1575      * @param response
1576      * @param methodToCall
1577      * @return
1578      * @throws Exception
1579      */
1580     public ActionForward promptBeforeValidation(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String methodToCall) throws Exception {
1581         KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
1582 
1583         /* callback to any pre rules check class */
1584         Class<? extends PromptBeforeValidation> promptBeforeValidationClass = getDataDictionaryService().getPromptBeforeValidationClass(kualiDocumentFormBase.getDocTypeName());
1585         if (LOG.isDebugEnabled()) {
1586             LOG.debug("PromptBeforeValidationClass: " + promptBeforeValidationClass);
1587         }
1588         if (promptBeforeValidationClass != null) {
1589             PromptBeforeValidation promptBeforeValidation = promptBeforeValidationClass.newInstance();
1590             PromptBeforeValidationEvent event = new PromptBeforeValidationEvent("Pre Maint route Check", "", kualiDocumentFormBase.getDocument());
1591             boolean continueRoute = promptBeforeValidation.processPrompts(form, request, event);
1592             if (!continueRoute) {
1593                 if (event.isPerformQuestion()) {
1594                     return super.performQuestionWithoutInput(mapping, kualiDocumentFormBase, request, response, event.getQuestionId(), event.getQuestionText(), event.getQuestionType(), methodToCall, event.getQuestionContext());
1595                 } else {
1596                     // This error section is here to avoid a silent and very confusing failure. If the PreRule
1597                     // instance returns a null for the processPreRuleChecks above, but does not set an
1598                     // ActionForwardName on the event, processing will just silently fail here, and the user
1599                     // will be presented with a blank frame.
1600                     //
1601                     // If the processPreRuleCheck() returns a false, an ActionForwardName needs to be set before hand
1602                     // by the PreRule class.
1603                     ActionForward actionForward = mapping.findForward(event.getActionForwardName());
1604                     if (actionForward == null) {
1605                         throw new RuntimeException("No ActionForwardName defined on this Event, no further actions will be processed.");
1606                     }
1607                     return actionForward;
1608                 }
1609             }
1610         }
1611 
1612         return null;
1613     }
1614 
1615 
1616     /**
1617      * Convenience method for building authorization exceptions
1618      *
1619      * @param action
1620      * @param document
1621      */
1622     protected DocumentAuthorizationException buildAuthorizationException(String action, Document document) {
1623         return new DocumentAuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(), action, document.getDocumentNumber());
1624     }
1625 
1626     protected boolean exitingDocument() {
1627     	String methodCalledViaDispatch = (String) GlobalVariables.getUserSession().retrieveObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_OBJECT_KEY);
1628         String methodCompleted = (String) GlobalVariables.getUserSession().retrieveObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_OBJECT_KEY);
1629         return StringUtils.isNotEmpty(methodCompleted) && StringUtils.isNotEmpty(methodCalledViaDispatch) && methodCompleted.startsWith(methodCalledViaDispatch);
1630     }
1631 
1632     protected void setupDocumentExit() {
1633     	String methodCalledViaDispatch = (String) GlobalVariables.getUserSession().retrieveObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_OBJECT_KEY);
1634     	if(StringUtils.isNotEmpty(methodCalledViaDispatch)) {
1635     		GlobalVariables.getUserSession().addObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_OBJECT_KEY, (Object) (methodCalledViaDispatch + DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_MARKER));
1636     	}
1637     }
1638 
1639     /**
1640      * If the given form has returnToActionList set to true, this method returns an ActionForward that should take the user back to
1641      * their action list; otherwise, it returns them to the portal.
1642      *
1643      * @param form
1644      * @return
1645      */
1646     protected ActionForward returnToSender(HttpServletRequest request, ActionMapping mapping, KualiDocumentFormBase form) {
1647         final ActionForward dest;
1648         if (form.isReturnToActionList()) {
1649             String workflowBase = getKualiConfigurationService().getPropertyValueAsString(
1650                     KRADConstants.WORKFLOW_URL_KEY);
1651             String actionListUrl = workflowBase + "/ActionList.do";
1652 
1653             dest = new ActionForward(actionListUrl, true);
1654         } else if (StringUtils.isNotBlank(form.getBackLocation())) {
1655             dest = new ActionForward(form.getBackLocation(), true);
1656         } else {
1657             dest = mapping.findForward(KRADConstants.MAPPING_PORTAL);
1658         }
1659 
1660         setupDocumentExit();
1661         return dest;
1662     }
1663 
1664     @SuppressWarnings("unchecked")
1665     protected void populateAuthorizationFields(KualiDocumentFormBase formBase) {
1666         if (formBase.isFormDocumentInitialized()) {
1667             Document document = formBase.getDocument();
1668             Person user = GlobalVariables.getUserSession().getPerson();
1669             DocumentPresentationController documentPresentationController = KNSServiceLocator
1670                     .getDocumentHelperService().getDocumentPresentationController(document);
1671             DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(document);
1672             Set<String> documentActions = documentPresentationController.getDocumentActions(document);
1673             documentActions = documentAuthorizer.getDocumentActions(document, user, documentActions);
1674 
1675             if (getDataDictionaryService().getDataDictionary().getDocumentEntry(document.getClass().getName()).getUsePessimisticLocking()) {
1676                 documentActions = getPessimisticLockService().getDocumentActions(document, user, documentActions);
1677             }
1678 
1679             //DocumentActionFlags flags = new DocumentActionFlags();
1680             formBase.setDocumentActions(convertSetToMap(documentActions));
1681 
1682         }
1683     }
1684 
1685     protected void populateAdHocActionRequestCodes(KualiDocumentFormBase formBase) {
1686         Document document = formBase.getDocument();
1687         DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(document);
1688         Map<String, String> adHocActionRequestCodes = new HashMap<String, String>();
1689 
1690         if (documentAuthorizer.canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_FYI_REQ, GlobalVariables.getUserSession().getPerson())) {
1691             adHocActionRequestCodes.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, KewApiConstants.ACTION_REQUEST_FYI_REQ_LABEL);
1692         }
1693         if (!document.getDocumentHeader().getWorkflowDocument().isFinal() && documentAuthorizer.canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, GlobalVariables.getUserSession().getPerson())) {
1694             adHocActionRequestCodes.put(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ_LABEL);
1695         }
1696         if (!(document.getDocumentHeader().getWorkflowDocument().isApproved() || document.getDocumentHeader().getWorkflowDocument().isProcessed() || document.getDocumentHeader().getWorkflowDocument().isFinal()) && documentAuthorizer.canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_APPROVE_REQ, GlobalVariables.getUserSession().getPerson())) {
1697             adHocActionRequestCodes.put(KewApiConstants.ACTION_REQUEST_APPROVE_REQ, KewApiConstants.ACTION_REQUEST_APPROVE_REQ_LABEL);
1698         }
1699 
1700         formBase.setAdHocActionRequestCodes(adHocActionRequestCodes);
1701 
1702     }
1703 
1704 
1705     @SuppressWarnings("unchecked")
1706     protected Map convertSetToMap(Set s) {
1707         Map map = new HashMap();
1708         Iterator i = s.iterator();
1709         while (i.hasNext()) {
1710             Object key = i.next();
1711             map.put(key, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
1712         }
1713         return map;
1714     }
1715 
1716     /**
1717      * @return the dataDictionaryService
1718      */
1719     protected DataDictionaryService getDataDictionaryService() {
1720         if (dataDictionaryService == null) {
1721             dataDictionaryService = KNSServiceLocator.getDataDictionaryService();
1722         }
1723         return dataDictionaryService;
1724     }
1725 
1726     protected DocumentHelperService getDocumentHelperService() {
1727         if (documentHelperService == null) {
1728             documentHelperService = KNSServiceLocator.getDocumentHelperService();
1729         }
1730         return this.documentHelperService;
1731     }
1732 
1733     protected DocumentService getDocumentService() {
1734         if (documentService == null) {
1735             documentService = KRADServiceLocatorWeb.getDocumentService();
1736         }
1737         return this.documentService;
1738     }
1739 
1740     protected ConfigurationService getKualiConfigurationService() {
1741         if (kualiConfigurationService == null) {
1742             kualiConfigurationService = KRADServiceLocator.getKualiConfigurationService();
1743         }
1744         return this.kualiConfigurationService;
1745     }
1746 
1747     protected ParameterService getParameterService() {
1748         if (parameterService == null) {
1749             parameterService = CoreFrameworkServiceLocator.getParameterService();
1750         }
1751         return this.parameterService;
1752     }
1753 
1754     protected PessimisticLockService getPessimisticLockService() {
1755         if (pessimisticLockService == null) {
1756             pessimisticLockService = KRADServiceLocatorWeb.getPessimisticLockService();
1757         }
1758         return this.pessimisticLockService;
1759     }
1760 
1761     protected KualiRuleService getKualiRuleService() {
1762         if (kualiRuleService == null) {
1763             kualiRuleService = KRADServiceLocatorWeb.getKualiRuleService();
1764         }
1765         return this.kualiRuleService;
1766     }
1767 
1768     protected GroupService getGroupService() {
1769         if (groupService == null) {
1770             groupService = KimApiServiceLocator.getGroupService();
1771         }
1772         return this.groupService;
1773     }
1774 
1775     protected AttachmentService getAttachmentService() {
1776         if (attachmentService == null) {
1777             attachmentService = KRADServiceLocator.getAttachmentService();
1778         }
1779         return this.attachmentService;
1780     }
1781 
1782     protected NoteService getNoteService() {
1783         if (noteService == null) {
1784             noteService = KRADServiceLocator.getNoteService();
1785         }
1786         return this.noteService;
1787     }
1788 
1789     protected BusinessObjectService getBusinessObjectService() {
1790         if (businessObjectService == null) {
1791             businessObjectService = KRADServiceLocator.getBusinessObjectService();
1792         }
1793         return this.businessObjectService;
1794     }
1795 
1796     @Override
1797     protected BusinessObjectAuthorizationService getBusinessObjectAuthorizationService() {
1798         if (businessObjectAuthorizationService == null) {
1799             businessObjectAuthorizationService = KNSServiceLocator.getBusinessObjectAuthorizationService();
1800         }
1801         return businessObjectAuthorizationService;
1802     }
1803 
1804     public BusinessObjectMetaDataService getBusinessObjectMetaDataService() {
1805         if (businessObjectMetaDataService == null) {
1806             businessObjectMetaDataService = KNSServiceLocator.getBusinessObjectMetaDataService();
1807         }
1808         return this.businessObjectMetaDataService;
1809     }
1810 
1811     public EntityManagerFactory getEntityManagerFactory() {
1812         if (entityManagerFactory == null) {
1813             entityManagerFactory = KRADServiceLocator.getApplicationEntityManagerFactory();
1814         }
1815         return this.entityManagerFactory;
1816     }
1817 
1818     /**
1819      * @see KualiAction#hideAllTabs(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
1820      */
1821     @Override
1822     public ActionForward hideAllTabs(ActionMapping mapping, ActionForm form,
1823                                      HttpServletRequest request, HttpServletResponse response)
1824             throws Exception {
1825         if (form instanceof KualiDocumentFormBase) {
1826             WebUtils.reuseErrorMapFromPreviousRequest((KualiDocumentFormBase) form);
1827         }
1828         return super.hideAllTabs(mapping, form, request, response);
1829     }
1830 
1831     /**
1832      * @see KualiAction#showAllTabs(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
1833      */
1834     @Override
1835     public ActionForward showAllTabs(ActionMapping mapping, ActionForm form,
1836                                      HttpServletRequest request, HttpServletResponse response)
1837             throws Exception {
1838         if (form instanceof KualiDocumentFormBase) {
1839             WebUtils.reuseErrorMapFromPreviousRequest((KualiDocumentFormBase) form);
1840         }
1841         return super.showAllTabs(mapping, form, request, response);
1842     }
1843 
1844     /**
1845      * @see KualiAction#toggleTab(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
1846      */
1847     @Override
1848     public ActionForward toggleTab(ActionMapping mapping, ActionForm form,
1849                                    HttpServletRequest request, HttpServletResponse response)
1850             throws Exception {
1851         if (form instanceof KualiDocumentFormBase) {
1852             WebUtils.reuseErrorMapFromPreviousRequest((KualiDocumentFormBase) form);
1853         }
1854         return super.toggleTab(mapping, form, request, response);
1855     }
1856 
1857     @Override
1858     protected void doProcessingAfterPost(KualiForm form, HttpServletRequest request) {
1859         super.doProcessingAfterPost(form, request);
1860         if (form instanceof KualiDocumentFormBase) {
1861             Document document = ((KualiDocumentFormBase) form).getDocument();
1862 
1863             getBusinessObjectService().linkUserFields(document);
1864         }
1865     }
1866 
1867     /**
1868      * Class that encapsulates the workflow for obtaining an reason from an action prompt.
1869      */
1870     private class ReasonPrompt {
1871         final String questionId;
1872         final String questionTextKey;
1873         final String questionType;
1874         final String missingReasonKey;
1875         final String questionCallerMapping;
1876         final String abortButton;
1877         final String noteIntroKey;
1878 
1879         private class Response {
1880             final String question;
1881             final ActionForward forward;
1882             final String reason;
1883             final String button;
1884             Response(String question, ActionForward forward) {
1885                 this(question, forward, null, null);
1886             }
1887             Response(String question, String reason, String button) {
1888                 this(question, null, reason, button);
1889             }
1890             private Response(String question, ActionForward forward, String reason, String button) {
1891                 this.question = question;
1892                 this.forward = forward;
1893                 this.reason = reason;
1894                 this.button = button;
1895             }
1896         }
1897 
1898         /**
1899          * @param questionId the question id/instance, 
1900          * @param questionTextKey application resources key for question text
1901          * @param questionType the {@link org.kuali.rice.kns.question.Question} question type
1902          * @param questionCallerMapping mapping of original action
1903          * @param abortButton button value considered to abort the prompt and return (optional, may be null)
1904          * @param noteIntroKey application resources key for quesiton text prefix (optional, may be null)
1905          */
1906         private ReasonPrompt(String questionId, String questionTextKey, String questionType, String missingReasonKey, String questionCallerMapping, String abortButton, String noteIntroKey) {
1907             this.questionId = questionId;
1908             this.questionTextKey = questionTextKey;
1909             this.questionType = questionType;
1910             this.questionCallerMapping = questionCallerMapping;
1911             this.abortButton = abortButton;
1912             this.noteIntroKey = noteIntroKey;
1913             this.missingReasonKey = missingReasonKey;
1914         }
1915 
1916         /**
1917          * Obtain a validated reason and button value via a Question prompt.  Reason is validated against
1918          * sensitive data patterns, and max Note text length
1919          * @param mapping Struts mapping
1920          * @param form Struts form
1921          * @param request http request
1922          * @param response http response
1923          * @return Response object representing *either*: 1) an ActionForward due to error or abort 2) a reason and button clicked
1924          * @throws Exception
1925          */
1926         public Response ask(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1927             String question = request.getParameter(KRADConstants.QUESTION_INST_ATTRIBUTE_NAME);
1928             String reason = request.getParameter(KRADConstants.QUESTION_REASON_ATTRIBUTE_NAME);
1929 
1930             if (StringUtils.isBlank(reason)) {
1931                 String context = request.getParameter(KRADConstants.QUESTION_CONTEXT);
1932                 if (context != null && StringUtils.contains(context, KRADConstants.QUESTION_REASON_ATTRIBUTE_NAME + "=")) {
1933                     reason = StringUtils.substringAfter(context, KRADConstants.QUESTION_REASON_ATTRIBUTE_NAME + "=");
1934                 }
1935             }
1936 
1937             String disapprovalNoteText = "";
1938 
1939             // start in logic for confirming the disapproval
1940             if (question == null) {
1941                 // ask question if not already asked
1942                 return new Response(question, performQuestionWithInput(mapping, form, request, response,
1943                         this.questionId,
1944                         getKualiConfigurationService().getPropertyValueAsString(this.questionTextKey),
1945                         this.questionType, this.questionCallerMapping, ""));
1946             }
1947 
1948             String buttonClicked = request.getParameter(KRADConstants.QUESTION_CLICKED_BUTTON);
1949             if (this.questionId.equals(question) && abortButton != null && abortButton.equals(buttonClicked)) {
1950                 // if no button clicked just reload the doc
1951                 return new Response(question, mapping.findForward(RiceConstants.MAPPING_BASIC));
1952             }
1953 
1954             // have to check length on value entered
1955             String introNoteMessage = "";
1956             if (noteIntroKey != null) {
1957                 introNoteMessage = getKualiConfigurationService().getPropertyValueAsString(this.noteIntroKey) + KRADConstants.BLANK_SPACE;
1958             }
1959 
1960             // build out full message
1961             disapprovalNoteText = introNoteMessage + reason;
1962 
1963             // check for sensitive data in note
1964             boolean warnForSensitiveData = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean(
1965                     KRADConstants.KNS_NAMESPACE, ParameterConstants.ALL_COMPONENT,
1966                     KRADConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS_WARNING_IND);
1967             if (warnForSensitiveData) {
1968                 String context = KRADConstants.QUESTION_REASON_ATTRIBUTE_NAME + "=" + reason;
1969                 ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response,
1970                         KRADConstants.QUESTION_REASON_ATTRIBUTE_NAME, disapprovalNoteText, this.questionCallerMapping, context);
1971                 if (forward != null) {
1972                     return new Response(question, forward);
1973                 }
1974             } else {
1975                 if (KRADUtils.containsSensitiveDataPatternMatch(disapprovalNoteText)) {
1976                     return new Response(question, performQuestionWithInputAgainBecauseOfErrors(mapping, form, request, response,
1977                             this.questionId, getKualiConfigurationService().getPropertyValueAsString(this.questionTextKey),
1978                             this.questionType, this.questionCallerMapping, "", reason,
1979                             RiceKeyConstants.ERROR_DOCUMENT_FIELD_CONTAINS_POSSIBLE_SENSITIVE_DATA,
1980                             KRADConstants.QUESTION_REASON_ATTRIBUTE_NAME, "reason"));
1981                 }
1982             }
1983 
1984             int disapprovalNoteTextLength = disapprovalNoteText.length();
1985 
1986             // get note text max length from DD
1987             int noteTextMaxLength = getDataDictionaryService().getAttributeMaxLength(Note.class, KRADConstants.NOTE_TEXT_PROPERTY_NAME);
1988 
1989             if (StringUtils.isBlank(reason) || (disapprovalNoteTextLength > noteTextMaxLength)) {
1990 
1991                 if (reason == null) {
1992                     // prevent a NPE by setting the reason to a blank string
1993                     reason = "";
1994                 }
1995                 return new Response(question, performQuestionWithInputAgainBecauseOfErrors(mapping, form, request, response,
1996                         this.questionId,
1997                         getKualiConfigurationService().getPropertyValueAsString(this.questionTextKey),
1998                         this.questionType, this.questionCallerMapping, "", reason,
1999                         this.missingReasonKey,
2000                         KRADConstants.QUESTION_REASON_ATTRIBUTE_NAME, Integer.toString(noteTextMaxLength)));
2001             }
2002 
2003             return new Response(question, disapprovalNoteText, buttonClicked);
2004         }
2005     }
2006     
2007     public ActionForward takeSuperUserActions(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
2008     	KualiDocumentFormBase documentForm = (KualiDocumentFormBase)form;
2009     	if(StringUtils.isBlank(documentForm.getSuperUserAnnotation())) {
2010     		GlobalVariables.getMessageMap().putErrorForSectionId("superuser.errors", "superuser.takeactions.annotation.missing", "");
2011     		return mapping.findForward(RiceConstants.MAPPING_BASIC);
2012     	} else if(documentForm.getSelectedActionRequests().isEmpty()) {
2013     		GlobalVariables.getMessageMap().putErrorForSectionId("superuser.errors", "superuser.takeactions.none.selected", "");
2014     		return mapping.findForward(RiceConstants.MAPPING_BASIC);
2015     	}
2016     	for(String actionRequestId : documentForm.getSelectedActionRequests()) {
2017     		ActionRequest actionRequest = null;
2018     		for(ActionRequest pendingActionRequest : documentForm.getActionRequests()) {
2019     			if(StringUtils.equals(pendingActionRequest.getId(), actionRequestId)) {
2020     				actionRequest = pendingActionRequest;
2021     				break;
2022     			}
2023     		}
2024     		if(actionRequest == null) {
2025     			// If the action request isn't pending then skip it
2026     			continue;
2027     		}
2028             WorkflowDocumentActionsService documentActions = getWorkflowDocumentActionsService(documentForm.getWorkflowDocument().getDocumentTypeId());
2029             DocumentActionParameters parameters = DocumentActionParameters.create(documentForm.getDocId(), GlobalVariables.getUserSession().getPrincipalId(), documentForm.getSuperUserAnnotation());
2030             documentActions.superUserTakeRequestedAction(parameters, true, actionRequestId);
2031             String messageString;
2032             if (StringUtils.equals(actionRequest.getActionRequested().getCode(), ActionRequestType.ACKNOWLEDGE.getCode())) {
2033                 messageString = "general.routing.superuser.actionRequestAcknowledged";
2034             } else if (StringUtils.equals(actionRequest.getActionRequested().getCode(), ActionRequestType.FYI.getCode())) {
2035                 messageString = "general.routing.superuser.actionRequestFYI";
2036             } else if (StringUtils.equals(actionRequest.getActionRequested().getCode(), ActionRequestType.COMPLETE.getCode())) {
2037                 messageString = "general.routing.superuser.actionRequestCompleted";
2038             } else if (StringUtils.equals(actionRequest.getActionRequested().getCode(), ActionRequestType.APPROVE.getCode())) {
2039                 messageString = "general.routing.superuser.actionRequestApproved";
2040             } else {
2041                 messageString = "general.routing.superuser.actionRequestApproved";
2042             }
2043             GlobalVariables.getMessageMap().putInfo("document", messageString, documentForm.getDocId(), actionRequestId);
2044             documentForm.setSuperUserAnnotation("");
2045     	}
2046     	return mapping.findForward(RiceConstants.MAPPING_BASIC);
2047     }
2048     
2049     public ActionForward superUserDisapprove(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
2050     	KualiDocumentFormBase documentForm = (KualiDocumentFormBase)form;
2051     	if(StringUtils.isBlank(documentForm.getSuperUserAnnotation())) {
2052     		GlobalVariables.getMessageMap().putErrorForSectionId("superuser.errors", "superuser.disapprove.annotation.missing", "");
2053     		return mapping.findForward(RiceConstants.MAPPING_BASIC);
2054     	}
2055         WorkflowDocumentActionsService documentActions = getWorkflowDocumentActionsService(documentForm.getWorkflowDocument().getDocumentTypeId());
2056         DocumentActionParameters parameters = DocumentActionParameters.create(documentForm.getDocId(), GlobalVariables.getUserSession().getPrincipalId(), documentForm.getSuperUserAnnotation());
2057         documentActions.superUserDisapprove(parameters, true);
2058         GlobalVariables.getMessageMap().putInfo("document", "general.routing.superuser.disapproved", documentForm.getDocId());
2059     	return mapping.findForward(RiceConstants.MAPPING_BASIC);
2060     }
2061     
2062     private WorkflowDocumentActionsService getWorkflowDocumentActionsService(String documentTypeId) {
2063         DocumentType documentType = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeById(documentTypeId);
2064         String applicationId = documentType.getApplicationId();
2065         QName serviceName = new QName(KewApiConstants.Namespaces.KEW_NAMESPACE_2_0,
2066                 KewApiConstants.ServiceNames.WORKFLOW_DOCUMENT_ACTIONS_SERVICE_SOAP);
2067         WorkflowDocumentActionsService service = (WorkflowDocumentActionsService) KsbApiServiceLocator.getServiceBus()
2068                 .getService(serviceName, applicationId);
2069         if (service == null) {
2070             service = KewApiServiceLocator.getWorkflowDocumentActionsService();
2071         }
2072         return service;
2073     }
2074 }
2075