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