View Javadoc

1   /*
2    * Copyright 2006-2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kns.web.struts.form;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.struts.action.ActionErrors;
20  import org.apache.struts.action.ActionMapping;
21  import org.apache.struts.upload.FormFile;
22  import org.kuali.rice.core.api.CoreApiServiceLocator;
23  import org.kuali.rice.core.api.util.RiceKeyConstants;
24  import org.kuali.rice.core.framework.services.CoreFrameworkServiceLocator;
25  import org.kuali.rice.core.web.format.NoOpStringFormatter;
26  import org.kuali.rice.core.web.format.TimestampAMPMFormatter;
27  import org.kuali.rice.kew.api.WorkflowDocument;
28  import org.kuali.rice.kew.exception.WorkflowException;
29  import org.kuali.rice.kim.api.identity.Person;
30  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
31  import org.kuali.rice.kim.util.KimConstants;
32  import org.kuali.rice.kns.datadictionary.HeaderNavigation;
33  import org.kuali.rice.kns.datadictionary.KNSDocumentEntry;
34  import org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry;
35  import org.kuali.rice.kns.util.WebUtils;
36  import org.kuali.rice.kns.web.derivedvaluesetter.DerivedValuesSetter;
37  import org.kuali.rice.kns.web.ui.HeaderField;
38  import org.kuali.rice.krad.bo.AdHocRoutePerson;
39  import org.kuali.rice.krad.bo.AdHocRouteWorkgroup;
40  import org.kuali.rice.krad.bo.Note;
41  import org.kuali.rice.krad.datadictionary.DataDictionary;
42  import org.kuali.rice.krad.document.Document;
43  import org.kuali.rice.krad.service.KRADServiceLocator;
44  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
45  import org.kuali.rice.krad.service.ModuleService;
46  import org.kuali.rice.krad.service.SessionDocumentService;
47  import org.kuali.rice.krad.util.GlobalVariables;
48  import org.kuali.rice.krad.util.KRADConstants;
49  import org.kuali.rice.krad.util.MessageMap;
50  import org.kuali.rice.krad.util.ObjectUtils;
51  import org.kuali.rice.krad.util.UrlFactory;
52  import org.springframework.util.AutoPopulatingList;
53  
54  import javax.servlet.http.HttpServletRequest;
55  import java.io.Serializable;
56  import java.util.ArrayList;
57  import java.util.HashMap;
58  import java.util.List;
59  import java.util.Map;
60  import java.util.Properties;
61  
62  /**
63   * TODO we should not be referencing kew constants from this class and wedding ourselves to that workflow application This class is
64   * the base action form for all documents.
65   */
66  public abstract class KualiDocumentFormBase extends KualiForm implements Serializable {
67      private static final long serialVersionUID = 916061016201941821L;
68  
69  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiDocumentFormBase.class);
70  
71      private Document document;
72      private String annotation = "";
73      private String command;
74  
75      private String docId;
76      private String docTypeName;
77  
78      private List<String> additionalScriptFiles;
79  
80      private AdHocRoutePerson newAdHocRoutePerson;
81      private AdHocRouteWorkgroup newAdHocRouteWorkgroup;
82  
83      private Note newNote;
84      
85      //TODO: is this still needed? I think it's obsolete now
86      private List boNotes;
87      
88      protected FormFile attachmentFile = new BlankFormFile();
89  
90      protected Map editingMode;
91      protected Map documentActions;
92      protected boolean suppressAllButtons;
93      
94      protected Map adHocActionRequestCodes;
95      private boolean returnToActionList;
96  
97      // for session enhancement
98      private String formKey;
99      private String docNum;
100     
101 
102     
103     /**
104      * Stores the error map from previous requests, so that we can continue to display error messages displayed during a previous request
105      */
106     private MessageMap errorMapFromPreviousRequest;
107     
108 	/***
109      * @see KualiForm#addRequiredNonEditableProperties()
110      */
111     @Override
112     public void addRequiredNonEditableProperties(){
113     	super.addRequiredNonEditableProperties();
114     	registerRequiredNonEditableProperty(KRADConstants.DOCUMENT_TYPE_NAME);
115     	registerRequiredNonEditableProperty(KRADConstants.FORM_KEY);
116     	registerRequiredNonEditableProperty(KRADConstants.NEW_NOTE_NOTE_TYPE_CODE);
117     }
118 
119 	/**
120 	 * @return the docNum
121 	 */
122 	public String getDocNum() {
123 		return this.docNum;
124 	}
125 
126 	/**
127 	 * @param docNum
128 	 *            the docNum to set
129 	 */
130 	public void setDocNum(String docNum) {
131 		this.docNum = docNum;
132 	}
133     
134     /**
135      * no args constructor that just initializes things for us
136      */
137     @SuppressWarnings("unchecked")
138 	public KualiDocumentFormBase() {
139         super();
140         
141         instantiateDocument();
142         newNote = new Note();
143         this.editingMode = new HashMap();
144         //this.additionalScriptFiles = new AutoPopulatingList(String.class);
145         this.additionalScriptFiles = new AutoPopulatingList<String>(String.class);
146 
147         // set the initial record for persons up
148         newAdHocRoutePerson = new AdHocRoutePerson();
149 
150         // set the initial record for workgroups up
151         newAdHocRouteWorkgroup = new AdHocRouteWorkgroup();
152 
153         // to make sure it posts back the correct time
154         setFormatterType("document.documentHeader.note.finDocNotePostedDttmStamp", TimestampAMPMFormatter.class);
155         setFormatterType("document.documentHeader.note.attachment.finDocNotePostedDttmStamp", TimestampAMPMFormatter.class);
156         //TODO: Chris - Notes: remove the above and change the below from boNotes when notes are finished
157         //overriding note formatter to make sure they post back the full timestamp
158         setFormatterType("document.documentHeader.boNote.notePostedTimestamp",TimestampAMPMFormatter.class);
159         setFormatterType("document.documentBusinessObject.boNote.notePostedTimestamp",TimestampAMPMFormatter.class);
160 
161         setFormatterType("editingMode", NoOpStringFormatter.class);
162         setFormatterType("editableAccounts", NoOpStringFormatter.class);
163 
164         setDocumentActions(new HashMap());
165         suppressAllButtons = false;
166         
167         initializeHeaderNavigationTabs();
168     }
169 
170     /**
171      * Setup workflow doc in the document.
172      */
173     @Override
174     public void populate(HttpServletRequest request) {
175         super.populate(request);
176 
177         WorkflowDocument workflowDocument = null;
178 
179         if (hasDocumentId()) {
180             // populate workflowDocument in documentHeader, if needed
181         	// KULRICE-4444 Obtain Document Header using the Workflow Service to minimize overhead
182             try {
183                 SessionDocumentService sessionDocumentService = KRADServiceLocatorWeb.getSessionDocumentService();
184             	workflowDocument = sessionDocumentService.getDocumentFromSession( GlobalVariables.getUserSession(), getDocument().getDocumentNumber());
185          	 	if ( workflowDocument == null)
186          	 	{
187                     // gets the workflow document from doc service, doc service will also set the workflow document in the
188                     // user's session
189          	 		Person person = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(KRADConstants.SYSTEM_USER);
190          	 		workflowDocument = KRADServiceLocatorWeb.getWorkflowDocumentService().loadWorkflowDocument(getDocument().getDocumentNumber(), person);
191          	 	 	sessionDocumentService.addDocumentToUserSession(GlobalVariables.getUserSession(), workflowDocument);
192          	 	 	if (workflowDocument == null)
193          	 	 	{
194          	 	 		throw new WorkflowException("Unable to retrieve workflow document # " + getDocument().getDocumentNumber() + " from workflow document service createWorkflowDocument");
195          	 	 	}
196          	 	 	else
197          	 	 	{
198          	 	 	LOG.debug("Retrieved workflow Document ID: " + workflowDocument.getDocumentId());
199          	 	 	}
200          	 	}
201 
202                 getDocument().getDocumentHeader().setWorkflowDocument(workflowDocument);
203             } catch (WorkflowException e) {
204                 LOG.warn("Error while instantiating workflowDoc", e);
205                 throw new RuntimeException("error populating documentHeader.workflowDocument", e);
206             }
207         } 
208         if (workflowDocument != null) {
209 	        //Populate Document Header attributes
210 	        populateHeaderFields(workflowDocument);
211         }
212     }
213     
214     protected String getPersonInquiryUrlLink(Person user, String linkBody) {
215         StringBuffer urlBuffer = new StringBuffer();
216         
217         if(user != null && StringUtils.isNotEmpty(linkBody) ) {
218         	ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(Person.class);
219         	Map<String, String[]> parameters = new HashMap<String, String[]>();
220         	parameters.put(KimConstants.AttributeConstants.PRINCIPAL_ID, new String[] { user.getPrincipalId() });
221         	String inquiryUrl = moduleService.getExternalizableBusinessObjectInquiryUrl(Person.class, parameters);
222             if(!StringUtils.equals(KimConstants.EntityTypes.SYSTEM, user.getEntityTypeCode())){
223 	            urlBuffer.append("<a href='");
224 	            urlBuffer.append(inquiryUrl);
225 	            urlBuffer.append("' ");
226 	            urlBuffer.append("target='_blank'");
227 	            urlBuffer.append("title='Person Inquiry'>");
228 	            urlBuffer.append(linkBody);
229 	            urlBuffer.append("</a>");
230             } else{
231             	urlBuffer.append(linkBody);
232             }
233         }
234         
235         return urlBuffer.toString();
236     }
237     
238     protected String getDocumentHandlerUrl(String documentId) {
239         Properties parameters = new Properties();
240         parameters.put(KRADConstants.PARAMETER_DOC_ID, documentId);
241         parameters.put(KRADConstants.PARAMETER_COMMAND, KRADConstants.METHOD_DISPLAY_DOC_SEARCH_VIEW);
242         return UrlFactory.parameterizeUrl(
243                 KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
244                         KRADConstants.WORKFLOW_URL_KEY) + "/" + KRADConstants.DOC_HANDLER_ACTION, parameters);
245     }
246     
247     protected String buildHtmlLink(String url, String linkBody) {
248         StringBuffer urlBuffer = new StringBuffer();
249         
250         if(StringUtils.isNotEmpty(url) && StringUtils.isNotEmpty(linkBody) ) {
251             urlBuffer.append("<a href='").append(url).append("'>").append(linkBody).append("</a>");
252         }
253         
254         return urlBuffer.toString();
255     }
256     
257     /**
258 	 * This method is used to populate the list of header field objects (see {@link KualiForm#getDocInfo()}) displayed on
259 	 * the Kuali document form display pages.
260 	 * 
261 	 * @param workflowDocument - the workflow document of the document being displayed (null is allowed)
262 	 */
263 	public void populateHeaderFields(WorkflowDocument workflowDocument) {
264 		getDocInfo().clear();
265 		getDocInfo().addAll(getStandardHeaderFields(workflowDocument));
266 	}
267 
268 	/**
269 	 * This method returns a list of {@link HeaderField} objects that are used by default on Kuali document display pages. To
270 	 * use this list and override an individual {@link HeaderField} object the id constants from
271 	 * {@link org.kuali.rice.krad.util.KRADConstants.DocumentFormHeaderFieldIds} can be used to identify items from the list.
272 	 * 
273 	 * @param workflowDocument - the workflow document of the document being displayed (null is allowed)
274 	 * @return a list of the standard fields displayed by default for all Kuali documents
275 	 */
276     protected List<HeaderField> getStandardHeaderFields(WorkflowDocument workflowDocument) {
277     	List<HeaderField> headerFields = new ArrayList<HeaderField>();
278     	setNumColumns(2);
279     	// check for a document template number as that will dictate column numbering
280     	HeaderField docTemplateNumber = null;
281         if ((ObjectUtils.isNotNull(getDocument())) && (ObjectUtils.isNotNull(getDocument().getDocumentHeader())) && (StringUtils.isNotBlank(getDocument().getDocumentHeader().getDocumentTemplateNumber()))) {
282 			String templateDocumentNumber = getDocument().getDocumentHeader().getDocumentTemplateNumber();
283 			docTemplateNumber = new HeaderField(KRADConstants.DocumentFormHeaderFieldIds.DOCUMENT_TEMPLATE_NUMBER, "DataDictionary.DocumentHeader.attributes.documentTemplateNumber",
284 					templateDocumentNumber,	buildHtmlLink(getDocumentHandlerUrl(templateDocumentNumber), templateDocumentNumber));
285 		}
286         //Document Number    	
287         HeaderField docNumber = new HeaderField("DataDictionary.DocumentHeader.attributes.documentNumber", workflowDocument != null? getDocument().getDocumentNumber() : null);
288         docNumber.setId(KRADConstants.DocumentFormHeaderFieldIds.DOCUMENT_NUMBER);
289         HeaderField docStatus = new HeaderField("DataDictionary.AttributeReferenceDummy.attributes.workflowDocumentStatus", workflowDocument != null? workflowDocument.getStatus().getLabel() : null);
290         docStatus.setId(KRADConstants.DocumentFormHeaderFieldIds.DOCUMENT_WORKFLOW_STATUS);
291         String initiatorNetworkId = null;
292         Person user = null;
293     	if (workflowDocument != null) {
294        		if (getInitiator() == null) {
295     			LOG.warn("User Not Found while attempting to build inquiry link for document header fields");
296     		} else {
297     			user = getInitiator();
298     			initiatorNetworkId = getInitiator().getPrincipalName();
299     		}
300     	}
301         String inquiryUrl = getPersonInquiryUrlLink(user, workflowDocument != null? initiatorNetworkId:null);
302 
303         HeaderField docInitiator = new HeaderField(KRADConstants.DocumentFormHeaderFieldIds.DOCUMENT_INITIATOR, "DataDictionary.AttributeReferenceDummy.attributes.initiatorNetworkId",
304         workflowDocument != null? initiatorNetworkId : null, workflowDocument != null? inquiryUrl : null);
305         
306         String createDateStr = null;
307         if(workflowDocument != null && workflowDocument.getDateCreated() != null) {
308             createDateStr = CoreApiServiceLocator.getDateTimeService().toString(workflowDocument.getDateCreated().toDate(), "hh:mm a MM/dd/yyyy");
309         }
310         
311         HeaderField docCreateDate = new HeaderField("DataDictionary.AttributeReferenceDummy.attributes.createDate", createDateStr);
312         docCreateDate.setId(KRADConstants.DocumentFormHeaderFieldIds.DOCUMENT_CREATE_DATE);
313         if (ObjectUtils.isNotNull(docTemplateNumber)) {
314         	setNumColumns(3);
315         }
316         
317         headerFields.add(docNumber);
318         headerFields.add(docStatus);
319         if (ObjectUtils.isNotNull(docTemplateNumber)) {
320         	headerFields.add(docTemplateNumber);
321         }
322         headerFields.add(docInitiator);
323         headerFields.add(docCreateDate);
324         if (ObjectUtils.isNotNull(docTemplateNumber)) {
325         	// adding an empty field so implementors do not have to worry about additional fields being put on the wrong row
326         	headerFields.add(HeaderField.EMPTY_FIELD);
327         }
328     	return headerFields;
329     }    
330 
331     /**
332      * @see org.apache.struts.action.ActionForm#validate(org.apache.struts.action.ActionMapping,
333      *      javax.servlet.http.HttpServletRequest)
334      */
335     @Override
336     public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
337         // check that annotation does not exceed 2000 characters
338         setAnnotation(StringUtils.stripToNull(getAnnotation()));
339         int diff = StringUtils.defaultString(getAnnotation()).length() - KRADConstants.DOCUMENT_ANNOTATION_MAX_LENGTH;
340         if (diff > 0) {
341             GlobalVariables.getMessageMap().putError("annotation", RiceKeyConstants.ERROR_DOCUMENT_ANNOTATION_MAX_LENGTH_EXCEEDED, new String[] { Integer.toString(KRADConstants.DOCUMENT_ANNOTATION_MAX_LENGTH), Integer.toString(diff) });
342         }
343         return super.validate(mapping, request);
344     }
345 
346     /**
347      * @return true if this document was properly initialized with a DocumentHeader and related KualiWorkflowDocument
348      */
349     final public boolean isFormDocumentInitialized() {
350         boolean initialized = false;
351 
352         if (document != null) {
353             if (document.getDocumentHeader() != null) {
354                 initialized = document.getDocumentHeader().hasWorkflowDocument();
355             }
356         }
357 
358         return initialized;
359     }
360 
361 
362     /**
363      * @return Map of editingModes for this document, as set during the most recent call to
364      *         populate(javax.servlet.http.HttpServletRequest)
365      */
366     @SuppressWarnings("unchecked")
367 	public Map getEditingMode() {
368         return editingMode;
369     }
370 
371     /**
372      * Set editingMode for this document
373      */
374     @SuppressWarnings("unchecked")
375 	public void setEditingMode(Map editingMode) {
376         this.editingMode = editingMode;
377     }
378     
379     /**
380 	 * @return the documentActions
381 	 */
382 	@SuppressWarnings("unchecked")
383 	public Map getDocumentActions() {
384 		return this.documentActions;
385 	}
386 
387 	/**
388 	 * @param documentActions the documentActions to set
389 	 */
390 	@SuppressWarnings("unchecked")
391 	public void setDocumentActions(Map documentActions) {
392 		this.documentActions = documentActions;
393 	}
394 	
395 	
396 
397 	/**
398 	 * @param adHocActionRequestCodes the adHocActionRequestCodes to set
399 	 */
400 	@SuppressWarnings("unchecked")
401 	public void setAdHocActionRequestCodes(Map adHocActionRequestCodes) {
402 		this.adHocActionRequestCodes = adHocActionRequestCodes;
403 	}
404 
405 	/**
406      * @return a map of the possible action request codes that takes into account the users context on the document
407      */
408     @SuppressWarnings("unchecked")
409 	public Map getAdHocActionRequestCodes() {
410         //Map adHocActionRequestCodes = new HashMap();
411         //KRADServiceLocatorInternal.getDocumentHelperService()
412         /*if (getWorkflowDocument() != null) {
413             if (getWorkflowDocument().isFYIRequested()) {
414                 adHocActionRequestCodes.put(KEWConstants.ACTION_REQUEST_FYI_REQ, KEWConstants.ACTION_REQUEST_FYI_REQ_LABEL);
415             }
416             else if (getWorkflowDocument().isAcknowledgeRequested()) {
417                 adHocActionRequestCodes.put(KEWConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, KEWConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ_LABEL);
418                 adHocActionRequestCodes.put(KEWConstants.ACTION_REQUEST_FYI_REQ, KEWConstants.ACTION_REQUEST_FYI_REQ_LABEL);
419             }
420             else if (getWorkflowDocument().isApprovalRequested() || getWorkflowDocument().isCompletionRequested() || getWorkflowDocument().stateIsInitiated() || getWorkflowDocument().stateIsSaved()) {
421                 adHocActionRequestCodes.put(KEWConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, KEWConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ_LABEL);
422                 adHocActionRequestCodes.put(KEWConstants.ACTION_REQUEST_FYI_REQ, KEWConstants.ACTION_REQUEST_FYI_REQ_LABEL);
423                 adHocActionRequestCodes.put(KEWConstants.ACTION_REQUEST_APPROVE_REQ, KEWConstants.ACTION_REQUEST_APPROVE_REQ_LABEL);
424             }
425         }*/
426         return adHocActionRequestCodes;
427     }
428 
429 
430     /**
431      * @return the list of ad hoc routing persons
432      */
433     public List<AdHocRoutePerson> getAdHocRoutePersons() {
434         return document.getAdHocRoutePersons();
435     }
436 
437 
438     /**
439      * @return attachmentFile
440      */
441     public FormFile getAttachmentFile() {
442         return attachmentFile;
443     }
444 
445     /**
446      * @param attachmentFile The attachmentFile to set.
447      */
448     public void setAttachmentFile(FormFile attachmentFile) {
449         this.attachmentFile = attachmentFile;
450     }
451 
452 
453     /**
454      * set the ad hoc routing persons list
455      *
456      * @param adHocRouteRecipients
457      */
458     public void setAdHocRoutePersons(List<AdHocRoutePerson> adHocRouteRecipients) {
459         document.setAdHocRoutePersons(adHocRouteRecipients);
460     }
461 
462     /**
463      * get the ad hoc routing workgroup requests
464      *
465      * @return
466      */
467     public List<AdHocRouteWorkgroup> getAdHocRouteWorkgroups() {
468         return document.getAdHocRouteWorkgroups();
469     }
470 
471     /**
472      * set the ad hoc routing workgroup requests
473      *
474      * @param adHocRouteWorkgroups
475      */
476     public void setAdHocRouteWorkgroups(List<AdHocRouteWorkgroup> adHocRouteWorkgroups) {
477         document.setAdHocRouteWorkgroups(adHocRouteWorkgroups);
478     }
479 
480     /**
481      * Special getter based on index to work with multi rows for ad hoc routing to persons struts page
482      *
483      * @param index
484      * @return
485      */
486     public AdHocRoutePerson getAdHocRoutePerson(int index) {
487         while (getAdHocRoutePersons().size() <= index) {
488             getAdHocRoutePersons().add(new AdHocRoutePerson());
489         }
490         return getAdHocRoutePersons().get(index);
491     }
492 
493     /**
494      * Special getter based on index to work with multi rows for ad hoc routing to workgroups struts page
495      *
496      * @param index
497      * @return
498      */
499     public AdHocRouteWorkgroup getAdHocRouteWorkgroup(int index) {
500         while (getAdHocRouteWorkgroups().size() <= index) {
501             getAdHocRouteWorkgroups().add(new AdHocRouteWorkgroup());
502         }
503         return getAdHocRouteWorkgroups().get(index);
504     }
505 
506     /**
507      * @return the new ad hoc route person object
508      */
509     public AdHocRoutePerson getNewAdHocRoutePerson() {
510         return newAdHocRoutePerson;
511     }
512 
513     /**
514      * set the new ad hoc route person object
515      *
516      * @param newAdHocRoutePerson
517      */
518     public void setNewAdHocRoutePerson(AdHocRoutePerson newAdHocRoutePerson) {
519         this.newAdHocRoutePerson = newAdHocRoutePerson;
520     }
521 
522     /**
523      * @return the new ad hoc route workgroup object
524      */
525     public AdHocRouteWorkgroup getNewAdHocRouteWorkgroup() {
526         return newAdHocRouteWorkgroup;
527     }
528 
529     /**
530      * set the new ad hoc route workgroup object
531      *
532      * @param newAdHocRouteWorkgroup
533      */
534     public void setNewAdHocRouteWorkgroup(AdHocRouteWorkgroup newAdHocRouteWorkgroup) {
535         this.newAdHocRouteWorkgroup = newAdHocRouteWorkgroup;
536     }
537 
538     /**
539      * @return Returns the Document
540      */
541     public Document getDocument() {
542         return document;
543     }
544 
545     /**
546      * @param document
547      */
548     public void setDocument(Document document) {
549         this.document = document;
550         if(document != null && StringUtils.isNotEmpty(document.getDocumentNumber())) {
551             populateHeaderFields(document.getDocumentHeader().getWorkflowDocument());
552         }
553     }
554 
555     /**
556      * @return WorkflowDocument for this form's document
557      */
558     public WorkflowDocument getWorkflowDocument() {
559         return getDocument().getDocumentHeader().getWorkflowDocument();
560     }
561     
562     /**
563 	 *  Null-safe check to see if the workflow document object exists before attempting to retrieve it.
564      *  (Which, if called, will throw an exception.)
565 	 */
566     public boolean isHasWorkflowDocument() {
567     	if ( getDocument() == null || getDocument().getDocumentHeader() == null ) {
568     		return false;
569     	}
570     	return getDocument().getDocumentHeader().hasWorkflowDocument();
571     }
572 
573     /**
574      * TODO rk implemented to account for caps coming from kuali user service from workflow
575      */
576     public boolean isUserDocumentInitiator() {
577         if (getWorkflowDocument() != null) {
578             return getWorkflowDocument().getInitiatorPrincipalId().equalsIgnoreCase(
579             		GlobalVariables.getUserSession().getPrincipalId());
580         }
581         return false;
582     }
583 
584     public Person getInitiator() {
585     	String initiatorPrincipalId = getWorkflowDocument().getInitiatorPrincipalId();
586     	return KimApiServiceLocator.getPersonService().getPerson(initiatorPrincipalId);
587     }
588 
589     /**
590      * @return true if the workflowDocument associated with this form is currently enroute
591      */
592     public boolean isDocumentEnRoute() {
593         return getWorkflowDocument().isEnroute();
594     }
595 
596     /**
597      * @param annotation The annotation to set.
598      */
599     public void setAnnotation(String annotation) {
600         this.annotation = annotation;
601     }
602 
603     /**
604      * @return Returns the annotation.
605      */
606     public String getAnnotation() {
607         return annotation;
608     }
609 
610     /**
611      * @return returns the command that was passed from workflow
612      */
613     public String getCommand() {
614         return command;
615     }
616 
617     /**
618      * setter for the command that was passed from workflow on the url
619      *
620      * @param command
621      */
622     public void setCommand(String command) {
623         this.command = command;
624     }
625 
626     /**
627      * @return returns the docId that was passed from workflow on the url
628      */
629     public String getDocId() {
630         return docId;
631     }
632 
633     /**
634      * setter for the docId that was passed from workflow on the url
635      *
636      * @param docId
637      */
638     public void setDocId(String docId) {
639         this.docId = docId;
640     }
641 
642     /**
643      * getter for the docTypeName that was passed from workflow on the url
644      *
645      * @return
646      */
647     public String getDocTypeName() {
648         return docTypeName;
649     }
650 
651     /**
652      * setter for the docTypeName that was passed from workflow on the url
653      *
654      * @param docTypeName
655      */
656     public void setDocTypeName(String docTypeName) {
657         this.docTypeName = docTypeName;
658     }
659 
660     /**
661      * getter for convenience that will return the initiators network id
662      *
663      * @return
664      */
665     public String getInitiatorNetworkId() {
666         return this.getWorkflowDocument().getInitiatorPrincipalId();
667     }
668 
669     /**
670      * Gets the suppressAllButtons attribute.
671      *
672      * @return Returns the suppressAllButtons.
673      */
674     public final boolean isSuppressAllButtons() {
675         return suppressAllButtons;
676     }
677 
678     /**
679      * Sets the suppressAllButtons attribute value.
680      *
681      * @param suppressAllButtons The suppressAllButtons to set.
682      */
683     public final void setSuppressAllButtons(boolean suppressAllButtons) {
684         this.suppressAllButtons = suppressAllButtons;
685     }
686 
687     /**
688      * @return true if this form's getDocument() method returns a Document, and if that Document's getDocumentHeaderId method
689      *         returns a non-null
690      */
691     public boolean hasDocumentId() {
692         boolean hasDocId = false;
693 
694         Document d = getDocument();
695         if (d != null) {
696             String docHeaderId = d.getDocumentNumber();
697 
698             hasDocId = StringUtils.isNotBlank(docHeaderId);
699         }
700 
701         return hasDocId;
702     }
703 
704     /**
705      * Sets flag indicating whether upon completion of approve, blanketApprove, cancel, or disapprove, the user should be returned
706      * to the actionList instead of to the portal
707      *
708      * @param returnToActionList
709      */
710     public void setReturnToActionList(boolean returnToActionList) {
711         this.returnToActionList = returnToActionList;
712     }
713 
714     public boolean isReturnToActionList() {
715         return returnToActionList;
716     }
717 
718     public List<String> getAdditionalScriptFiles() {
719         return additionalScriptFiles;
720     }
721 
722     public void setAdditionalScriptFiles(List<String> additionalScriptFiles) {
723         this.additionalScriptFiles = additionalScriptFiles;
724     }
725 
726     public void setAdditionalScriptFile( int index, String scriptFile ) {
727         additionalScriptFiles.set( index, scriptFile );
728 	}
729 
730     public String getAdditionalScriptFile( int index ) {
731         return additionalScriptFiles.get( index );
732     }
733 
734     public Note getNewNote() {
735         return newNote;
736     }
737 
738     public void setNewNote(Note newNote) {
739         this.newNote = newNote;
740     }
741 
742     /**
743      * Gets the boNotes attribute. 
744      * @return Returns the boNotes.
745      */
746     @SuppressWarnings("unchecked")
747 	public List getBoNotes() {
748         return boNotes;
749     }
750 
751     /**
752      * Sets the boNotes attribute value.
753      * @param boNotes The boNotes to set.
754      */
755     @SuppressWarnings("unchecked")
756 	public void setBoNotes(List boNotes) {
757         this.boNotes = boNotes;
758     }
759 
760     public String getFormKey() {
761         return this.formKey;
762     }
763 
764     public void setFormKey(String formKey) {
765         this.formKey = formKey;
766     }
767 
768     /* Reset method
769      * This is initially created for session document implementation
770      * @param mapping
771      * @param request
772      */
773     @Override
774     public void reset(ActionMapping mapping, HttpServletRequest request) {
775     	super.reset(mapping, request);
776         this.setMethodToCall(null);
777         this.setRefreshCaller(null);
778         this.setAnchor(null);
779         this.setCurrentTabIndex(0);
780         
781     }
782 
783     
784     /**
785      * Adds the attachment file size to the list of max file sizes.
786      * 
787      * @see org.kuali.rice.krad.web.struts.pojo.PojoFormBase#customInitMaxUploadSizes()
788      */
789     @Override
790     protected void customInitMaxUploadSizes() {
791         super.customInitMaxUploadSizes();
792         addMaxUploadSize(CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(KRADConstants.KRAD_NAMESPACE, KRADConstants.DetailTypes.DOCUMENT_DETAIL_TYPE, KRADConstants.ATTACHMENT_MAX_FILE_SIZE_PARM_NM));
793     }
794 
795     
796     
797 	/**
798 	 * This overridden method ...
799 	 * IMPORTANT: any overrides of this method must ensure that nothing in the HTTP request will be used to determine whether document is in session 
800 	 * 
801 	 * @see org.kuali.rice.krad.web.struts.pojo.PojoFormBase#shouldPropertyBePopulatedInForm(java.lang.String, javax.servlet.http.HttpServletRequest)
802 	 */
803 	@Override
804 	public boolean shouldPropertyBePopulatedInForm(String requestParameterName, HttpServletRequest request) {
805 		for ( String prefix : KRADConstants.ALWAYS_VALID_PARAMETER_PREFIXES ) {
806 			if (requestParameterName.startsWith(prefix)) {
807 				return true;
808 			}
809 		}
810 
811 		if (StringUtils.equalsIgnoreCase(getMethodToCall(), KRADConstants.DOC_HANDLER_METHOD)) {
812 			return true;
813 		}
814 		if (WebUtils.isDocumentSession(getDocument(), this)) {
815 			return isPropertyEditable(requestParameterName) || isPropertyNonEditableButRequired(requestParameterName);
816 		}
817 		return true;
818 	}
819 
820 	/**
821 	 * This overridden method ...
822 	 * 
823 	 * @see KualiForm#shouldMethodToCallParameterBeUsed(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest)
824 	 */
825 	@Override
826 	public boolean shouldMethodToCallParameterBeUsed(
827 			String methodToCallParameterName,
828 			String methodToCallParameterValue, HttpServletRequest request) {
829 		if (StringUtils.equals(methodToCallParameterName, KRADConstants.DISPATCH_REQUEST_PARAMETER) &&
830 				StringUtils.equals(methodToCallParameterValue, KRADConstants.DOC_HANDLER_METHOD)) {
831 			return true;
832 		}
833 		return super.shouldMethodToCallParameterBeUsed(methodToCallParameterName,
834 				methodToCallParameterValue, request);
835 	}
836 	
837 	public MessageMap getMessageMapFromPreviousRequest() {
838 		return this.errorMapFromPreviousRequest;
839 	}
840 	
841 	public void setMessageMapFromPreviousRequest(MessageMap errorMapFromPreviousRequest) {
842 		this.errorMapFromPreviousRequest = errorMapFromPreviousRequest;
843 	}
844 	
845 	@Override
846 	public void setDerivedValuesOnForm(HttpServletRequest request) {
847 		super.setDerivedValuesOnForm(request);
848 
849 		String docTypeName = getDocTypeName();
850 		if (StringUtils.isNotBlank(docTypeName)) {
851 			DataDictionary dataDictionary = KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary();
852 
853             Class<? extends DerivedValuesSetter> derivedValuesSetterClass = null;
854             KNSDocumentEntry documentEntry = (KNSDocumentEntry) dataDictionary.getDocumentEntry(docTypeName);
855             derivedValuesSetterClass = (documentEntry).getDerivedValuesSetterClass();
856 
857 			if (derivedValuesSetterClass != null) {
858 				DerivedValuesSetter derivedValuesSetter = null;
859 				try {
860 					derivedValuesSetter = derivedValuesSetterClass.newInstance();
861 				}
862 
863 				catch (Exception e) {
864 					LOG.error("Unable to instantiate class " + derivedValuesSetterClass.getName(), e);
865 					throw new RuntimeException("Unable to instantiate class " + derivedValuesSetterClass.getName(), e);
866 				}
867 				derivedValuesSetter.setDerivedValues(this, request);
868 			}
869 		}
870 	}
871 	
872 	protected String getDefaultDocumentTypeName() {
873 		return "";
874 	}
875 	
876 	/** will instatiate a new document setting it on the form if {@link KualiDocumentFormBase#getDefaultDocumentTypeName()} is overriden to return a valid value. */
877 	protected void instantiateDocument() {
878 		if (document == null && StringUtils.isNotBlank(getDefaultDocumentTypeName())) {
879 			Class<? extends Document> documentClass = getDocumentClass();
880 			try {
881 				Document document = documentClass.newInstance();
882 				setDocument(document);
883 			} catch (Exception e) {
884 				LOG.error("Unable to instantiate document class " + documentClass.getName() + " document type " + getDefaultDocumentTypeName());
885 				throw new RuntimeException(e);
886 			}
887 		}
888 	}
889 	
890 	/** gets the document class from the datadictionary if {@link KualiDocumentFormBase#getDefaultDocumentTypeName()} is overriden to return a valid value otherwise behavior is nondeterministic. */
891 	private Class<? extends Document> getDocumentClass() {
892 		return KRADServiceLocatorWeb.getDataDictionaryService().getDocumentClassByTypeName(getDefaultDocumentTypeName());
893 	}
894 	
895 	/**initializes the header tabs from what is defined in the datadictionary if {@link KualiDocumentFormBase#getDefaultDocumentTypeName()} is overriden to return a valid value. */
896     protected void initializeHeaderNavigationTabs() {
897     	if (StringUtils.isNotBlank(getDefaultDocumentTypeName())) {
898     		final KNSDocumentEntry docEntry = (KNSDocumentEntry) KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(getDocumentClass().getName());
899     		final List<HeaderNavigation> navList = docEntry.getHeaderNavigationList();
900     		final HeaderNavigation[] list = new HeaderNavigation[navList.size()];
901     		super.setHeaderNavigationTabs(navList.toArray(list));
902     	}
903     } 
904 }