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