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