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}