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.krad.web.controller; 017 018import org.apache.commons.lang.ArrayUtils; 019import org.apache.commons.lang.StringUtils; 020import org.kuali.rice.core.api.config.property.ConfigurationService; 021import org.kuali.rice.core.api.exception.RiceRuntimeException; 022import org.kuali.rice.core.api.util.RiceKeyConstants; 023import org.kuali.rice.kew.api.KewApiConstants; 024import org.kuali.rice.kew.api.WorkflowDocument; 025import org.kuali.rice.kew.api.exception.WorkflowException; 026import org.kuali.rice.kim.api.identity.Person; 027import org.kuali.rice.krad.UserSessionUtils; 028import org.kuali.rice.krad.bo.AdHocRouteRecipient; 029import org.kuali.rice.krad.bo.Attachment; 030import org.kuali.rice.krad.bo.DocumentHeader; 031import org.kuali.rice.krad.bo.Note; 032import org.kuali.rice.krad.document.Document; 033import org.kuali.rice.krad.document.DocumentAuthorizer; 034import org.kuali.rice.krad.maintenance.MaintenanceDocument; 035import org.kuali.rice.krad.exception.DocumentAuthorizationException; 036import org.kuali.rice.krad.exception.UnknownDocumentIdException; 037import org.kuali.rice.krad.exception.ValidationException; 038import org.kuali.rice.krad.rules.rule.event.AddNoteEvent; 039import org.kuali.rice.krad.service.AttachmentService; 040import org.kuali.rice.krad.service.BusinessObjectService; 041import org.kuali.rice.krad.service.DataDictionaryService; 042import org.kuali.rice.krad.service.DocumentDictionaryService; 043import org.kuali.rice.krad.service.DocumentService; 044import org.kuali.rice.krad.service.KRADServiceLocator; 045import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 046import org.kuali.rice.krad.service.NoteService; 047import org.kuali.rice.krad.uif.UifConstants.WorkflowAction; 048import org.kuali.rice.krad.uif.UifParameters; 049import org.kuali.rice.krad.uif.UifPropertyPaths; 050import org.kuali.rice.krad.uif.container.CollectionGroup; 051import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 052import org.kuali.rice.krad.util.GlobalVariables; 053import org.kuali.rice.krad.util.KRADConstants; 054import org.kuali.rice.krad.util.NoteType; 055import org.kuali.rice.krad.web.form.DocumentFormBase; 056import org.kuali.rice.krad.web.form.UifFormBase; 057import org.springframework.util.FileCopyUtils; 058import org.springframework.validation.BindingResult; 059import org.springframework.web.bind.ServletRequestBindingException; 060import org.springframework.web.bind.annotation.ModelAttribute; 061import org.springframework.web.bind.annotation.RequestMapping; 062import org.springframework.web.bind.annotation.RequestMethod; 063import org.springframework.web.multipart.MultipartFile; 064import org.springframework.web.servlet.ModelAndView; 065 066import javax.servlet.http.HttpServletRequest; 067import javax.servlet.http.HttpServletResponse; 068import java.io.FileNotFoundException; 069import java.io.IOException; 070import java.io.InputStream; 071import java.util.ArrayList; 072import java.util.List; 073import java.util.Properties; 074 075/** 076 * Base controller class for all KRAD <code>DocumentView</code> screens working 077 * with <code>Document</code> models 078 * 079 * <p> 080 * Provides default controller implementations for the standard document actions including: doc handler 081 * (retrieve from doc search and action list), save, route (and other KEW actions) 082 * </p> 083 * 084 * @author Kuali Rice Team (rice.collab@kuali.org) 085 */ 086public abstract class DocumentControllerBase extends UifControllerBase { 087 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentControllerBase.class); 088 089 // COMMAND constants which cause docHandler to load an existing document 090 // instead of creating a new one 091 protected static final String[] DOCUMENT_LOAD_COMMANDS = 092 {KewApiConstants.ACTIONLIST_COMMAND, KewApiConstants.DOCSEARCH_COMMAND, KewApiConstants.SUPERUSER_COMMAND, 093 KewApiConstants.HELPDESK_ACTIONLIST_COMMAND}; 094 095 private BusinessObjectService businessObjectService; 096 private DataDictionaryService dataDictionaryService; 097 private DocumentService documentService; 098 private DocumentDictionaryService documentDictionaryService; 099 private AttachmentService attachmentService; 100 private NoteService noteService; 101 102 /** 103 * @see org.kuali.rice.krad.web.controller.UifControllerBase#createInitialForm(javax.servlet.http.HttpServletRequest) 104 */ 105 @Override 106 protected abstract DocumentFormBase createInitialForm(HttpServletRequest request); 107 108 /** 109 * Used to funnel all document handling through, we could do useful things 110 * like log and record various openings and status Additionally it may be 111 * nice to have a single dispatcher that can know how to dispatch to a 112 * redirect url for document specific handling but we may not need that as 113 * all we should need is the document to be able to load itself based on 114 * document id and then which action forward or redirect is pertinent for 115 * the document type. 116 */ 117 @RequestMapping(params = "methodToCall=docHandler") 118 public ModelAndView docHandler(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, 119 HttpServletRequest request, HttpServletResponse response) throws Exception { 120 String command = form.getCommand(); 121 122 // in all of the following cases we want to load the document 123 if (ArrayUtils.contains(DOCUMENT_LOAD_COMMANDS, command) && form.getDocId() != null) { 124 loadDocument(form); 125 } else if (KewApiConstants.INITIATE_COMMAND.equals(command)) { 126 createDocument(form); 127 } else { 128 LOG.error("docHandler called with invalid parameters"); 129 throw new IllegalArgumentException("docHandler called with invalid parameters"); 130 } 131 132 // TODO: authorization on document actions 133 // if (KEWConstants.SUPERUSER_COMMAND.equalsIgnoreCase(command)) { 134 // form.setSuppressAllButtons(true); 135 // } 136 137 return getUIFModelAndView(form); 138 } 139 140 /** 141 * Loads the document by its provided document header id. This has been abstracted out so that 142 * it can be overridden in children if the need arises 143 * 144 * @param form - form instance that contains the doc id parameter and where 145 * the retrieved document instance should be set 146 */ 147 protected void loadDocument(DocumentFormBase form) throws WorkflowException { 148 String docId = form.getDocId(); 149 150 LOG.debug("Loading document" + docId); 151 152 Document doc = null; 153 doc = getDocumentService().getByDocumentHeaderId(docId); 154 if (doc == null) { 155 throw new UnknownDocumentIdException( 156 "Document no longer exists. It may have been cancelled before being saved."); 157 } 158 159 WorkflowDocument workflowDocument = doc.getDocumentHeader().getWorkflowDocument(); 160 if (!getDocumentDictionaryService().getDocumentAuthorizer(doc).canOpen(doc, 161 GlobalVariables.getUserSession().getPerson())) { 162 throw buildAuthorizationException("open", doc); 163 } 164 165 // re-retrieve the document using the current user's session - remove 166 // the system user from the WorkflowDcument object 167 if (workflowDocument != doc.getDocumentHeader().getWorkflowDocument()) { 168 LOG.warn("Workflow document changed via canOpen check"); 169 doc.getDocumentHeader().setWorkflowDocument(workflowDocument); 170 } 171 172 form.setDocument(doc); 173 WorkflowDocument workflowDoc = doc.getDocumentHeader().getWorkflowDocument(); 174 form.setDocTypeName(workflowDoc.getDocumentTypeName()); 175 176 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), workflowDoc); 177 } 178 179 /** 180 * Creates a new document of the type specified by the docTypeName property of the given form. 181 * This has been abstracted out so that it can be overridden in children if the need arises. 182 * 183 * @param form - form instance that contains the doc type parameter and where 184 * the new document instance should be set 185 */ 186 protected void createDocument(DocumentFormBase form) throws WorkflowException { 187 LOG.debug("Creating new document instance for doc type: " + form.getDocTypeName()); 188 Document doc = getDocumentService().getNewDocument(form.getDocTypeName()); 189 190 form.setDocument(doc); 191 form.setDocTypeName(doc.getDocumentHeader().getWorkflowDocument().getDocumentTypeName()); 192 } 193 194 /** 195 * Reloads the document contained on the form from the database 196 * 197 * @param form - document form base containing the document instance from which the document number will 198 * be retrieved and used to fetch the document from the database 199 * @return ModelAndView 200 */ 201 @RequestMapping(params = "methodToCall=reload") 202 public ModelAndView reload(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, 203 HttpServletRequest request, HttpServletResponse response) throws Exception { 204 Document document = form.getDocument(); 205 206 // prepare for the reload action - set doc id and command 207 form.setDocId(document.getDocumentNumber()); 208 form.setCommand(DOCUMENT_LOAD_COMMANDS[1]); 209 210 GlobalVariables.getMessageMap().putInfo(KRADConstants.GLOBAL_MESSAGES, RiceKeyConstants.MESSAGE_RELOADED); 211 212 // forward off to the doc handler 213 return docHandler(form, result, request, response); 214 } 215 216 /** 217 * Prompts user to confirm the cancel action then if confirmed cancels the document instance 218 * contained on the form 219 * 220 * @param form - document form base containing the document instance that will be cancelled 221 * @return ModelAndView 222 */ 223 @RequestMapping(params = "methodToCall=cancel") 224 @Override 225 public ModelAndView cancel(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result, 226 HttpServletRequest request, HttpServletResponse response) { 227 DocumentFormBase documentForm = (DocumentFormBase) form; 228 229 // TODO: prompt user to confirm the cancel, need question framework 230 231 performWorkflowAction(documentForm, WorkflowAction.CANCEL, false); 232 233 return returnToPrevious(form); 234 } 235 236 /** 237 * Saves the document instance contained on the form 238 * 239 * @param form - document form base containing the document instance that will be saved 240 * @return ModelAndView 241 */ 242 @RequestMapping(params = "methodToCall=save") 243 public ModelAndView save(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, 244 HttpServletRequest request, HttpServletResponse response) throws Exception { 245 performWorkflowAction(form, WorkflowAction.SAVE, true); 246 247 return getUIFModelAndView(form); 248 } 249 250 /** 251 * Completes the document instance contained on the form 252 * 253 * @param form - document form base containing the document instance that will be completed 254 * @return ModelAndView 255 */ 256 @RequestMapping(params = "methodToCall=complete") 257 public ModelAndView complete(@ModelAttribute("KualiForm") 258 DocumentFormBase form, BindingResult result, HttpServletRequest request, HttpServletResponse response) throws Exception { 259 performWorkflowAction(form, WorkflowAction.COMPLETE, true); 260 261 return getUIFModelAndView(form); 262 } 263 264 /** 265 * Routes the document instance contained on the form 266 * 267 * @param form - document form base containing the document instance that will be routed 268 * @return ModelAndView 269 */ 270 @RequestMapping(params = "methodToCall=route") 271 public ModelAndView route(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, 272 HttpServletRequest request, HttpServletResponse response) { 273 performWorkflowAction(form, WorkflowAction.ROUTE, true); 274 275 return getUIFModelAndView(form); 276 } 277 278 /** 279 * Performs the blanket approve workflow action on the form document instance 280 * 281 * @param form - document form base containing the document instance that will be blanket approved 282 * @return ModelAndView 283 */ 284 @RequestMapping(params = "methodToCall=blanketApprove") 285 public ModelAndView blanketApprove(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, 286 HttpServletRequest request, HttpServletResponse response) throws Exception { 287 performWorkflowAction(form, WorkflowAction.BLANKETAPPROVE, true); 288 289 return returnToPrevious(form); 290 } 291 292 /** 293 * Performs the approve workflow action on the form document instance 294 * 295 * @param form - document form base containing the document instance that will be approved 296 * @return ModelAndView 297 */ 298 @RequestMapping(params = "methodToCall=approve") 299 public ModelAndView approve(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, 300 HttpServletRequest request, HttpServletResponse response) throws Exception { 301 performWorkflowAction(form, WorkflowAction.APPROVE, true); 302 303 return returnToPrevious(form); 304 } 305 306 /** 307 * Performs the disapprove workflow action on the form document instance 308 * 309 * @param form - document form base containing the document instance that will be disapproved 310 * @return ModelAndView 311 */ 312 @RequestMapping(params = "methodToCall=disapprove") 313 public ModelAndView disapprove(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, 314 HttpServletRequest request, HttpServletResponse response) throws Exception { 315 // TODO: need to prompt for disapproval note text 316 performWorkflowAction(form, WorkflowAction.DISAPPROVE, true); 317 318 return returnToPrevious(form); 319 } 320 321 /** 322 * Performs the fyi workflow action on the form document instance 323 * 324 * @param form - document form base containing the document instance the fyi will be taken on 325 * @return ModelAndView 326 */ 327 @RequestMapping(params = "methodToCall=fyi") 328 public ModelAndView fyi(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, 329 HttpServletRequest request, HttpServletResponse response) throws Exception { 330 performWorkflowAction(form, WorkflowAction.FYI, false); 331 332 return returnToPrevious(form); 333 } 334 335 /** 336 * Performs the acknowledge workflow action on the form document instance 337 * 338 * @param form - document form base containing the document instance the acknowledge will be taken on 339 * @return ModelAndView 340 */ 341 @RequestMapping(params = "methodToCall=acknowledge") 342 public ModelAndView acknowledge(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, 343 HttpServletRequest request, HttpServletResponse response) throws Exception { 344 performWorkflowAction(form, WorkflowAction.ACKNOWLEDGE, false); 345 346 return returnToPrevious(form); 347 } 348 349 /** 350 * Invokes the {@link DocumentService} to carry out a request workflow action and adds a success message, if 351 * requested a check for sensitive data is also performed 352 * 353 * @param form - document form instance containing the document for which the action will be taken on 354 * @param action - {@link WorkflowAction} enum indicating what workflow action to take 355 * @param checkSensitiveData - boolean indicating whether a check for sensitive data should occur 356 */ 357 protected void performWorkflowAction(DocumentFormBase form, WorkflowAction action, boolean checkSensitiveData) { 358 Document document = form.getDocument(); 359 360 LOG.debug("Performing workflow action " + action.name() + "for document: " + document.getDocumentNumber()); 361 362 // TODO: need question and prompt framework 363 if (checkSensitiveData) { 364 // String viewName = checkAndWarnAboutSensitiveData(form, request, response, 365 // KRADPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "route", ""); 366 // if (viewName != null) { 367 // return new ModelAndView(viewName); 368 // } 369 } 370 371 try { 372 String successMessageKey = null; 373 switch (action) { 374 case SAVE: 375 getDocumentService().saveDocument(document); 376 successMessageKey = RiceKeyConstants.MESSAGE_SAVED; 377 break; 378 case ROUTE: 379 getDocumentService().routeDocument(document, form.getAnnotation(), combineAdHocRecipients(form)); 380 successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_SUCCESSFUL; 381 break; 382 case BLANKETAPPROVE: 383 getDocumentService().blanketApproveDocument(document, form.getAnnotation(), combineAdHocRecipients( 384 form)); 385 successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_APPROVED; 386 break; 387 case APPROVE: 388 getDocumentService().approveDocument(document, form.getAnnotation(), combineAdHocRecipients(form)); 389 successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_APPROVED; 390 break; 391 case DISAPPROVE: 392 // TODO: need to get disapprove note from user 393 String disapprovalNoteText = ""; 394 getDocumentService().disapproveDocument(document, disapprovalNoteText); 395 successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_DISAPPROVED; 396 break; 397 case FYI: 398 getDocumentService().clearDocumentFyi(document, combineAdHocRecipients(form)); 399 successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_FYIED; 400 break; 401 case ACKNOWLEDGE: 402 getDocumentService().acknowledgeDocument(document, form.getAnnotation(), combineAdHocRecipients( 403 form)); 404 successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_ACKNOWLEDGED; 405 break; 406 case CANCEL: 407 if (getDocumentService().documentExists(document.getDocumentNumber())) { 408 getDocumentService().cancelDocument(document, form.getAnnotation()); 409 successMessageKey = RiceKeyConstants.MESSAGE_CANCELLED; 410 } 411 break; 412 case COMPLETE: 413 if (getDocumentService().documentExists(document.getDocumentNumber())) { 414 getDocumentService().completeDocument(document, form.getAnnotation(), combineAdHocRecipients(form)); 415 successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_SUCCESSFUL; 416 } 417 break; 418 } 419 420 if (successMessageKey != null) { 421 GlobalVariables.getMessageMap().putInfo(KRADConstants.GLOBAL_MESSAGES, successMessageKey); 422 } 423 } catch (ValidationException e) { 424 // if errors in map, swallow exception so screen will draw with errors 425 // if not then throw runtime because something bad happened 426 if (GlobalVariables.getMessageMap().hasNoErrors()) { 427 throw new RiceRuntimeException("Validation Exception with no error message.", e); 428 } 429 } catch (Exception e) { 430 throw new RiceRuntimeException( 431 "Exception trying to invoke action " + action.name() + "for document: " + document 432 .getDocumentNumber(), e); 433 } 434 435 form.setAnnotation(""); 436 } 437 438 /** 439 * Redirects to the supervisor functions page 440 * 441 * @return ModelAndView - model and view configured for the redirect URL 442 */ 443 @RequestMapping(params = "methodToCall=supervisorFunctions") 444 public ModelAndView supervisorFunctions(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, 445 HttpServletRequest request, HttpServletResponse response) throws Exception { 446 447 String workflowSuperUserUrl = getConfigurationService().getPropertyValueAsString(KRADConstants.WORKFLOW_URL_KEY) 448 + "/" + KRADConstants.SUPERUSER_ACTION; 449 450 Properties props = new Properties(); 451 props.put(UifParameters.METHOD_TO_CALL, "displaySuperUserDocument"); 452 props.put(UifPropertyPaths.DOCUMENT_ID, form.getDocument().getDocumentNumber()); 453 454 return performRedirect(form, workflowSuperUserUrl, props); 455 } 456 457 /** 458 * Called by the add note action for adding a note. Method validates, saves attachment and adds the 459 * time stamp and author. Calls the UifControllerBase.addLine method to handle generic actions. 460 * 461 * @param uifForm - document form base containing the note instance that will be inserted into the document 462 * @return ModelAndView 463 */ 464 @RequestMapping(method = RequestMethod.POST, params = "methodToCall=insertNote") 465 public ModelAndView insertNote(@ModelAttribute("KualiForm") UifFormBase uifForm, BindingResult result, 466 HttpServletRequest request, HttpServletResponse response) { 467 468 // Get the note add line 469 String selectedCollectionPath = uifForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH); 470 CollectionGroup collectionGroup = uifForm.getPostedView().getViewIndex().getCollectionGroupByPath( 471 selectedCollectionPath); 472 String addLinePath = collectionGroup.getAddLineBindingInfo().getBindingPath(); 473 Object addLine = ObjectPropertyUtils.getPropertyValue(uifForm, addLinePath); 474 Note newNote = (Note) addLine; 475 newNote.setNotePostedTimestampToCurrent(); 476 477 Document document = ((DocumentFormBase) uifForm).getDocument(); 478 479 newNote.setRemoteObjectIdentifier(document.getNoteTarget().getObjectId()); 480 481 // Get the attachment file 482 String attachmentTypeCode = null; 483 MultipartFile attachmentFile = uifForm.getAttachmentFile(); 484 Attachment attachment = null; 485 if (attachmentFile != null && !StringUtils.isBlank(attachmentFile.getOriginalFilename())) { 486 if (attachmentFile.getSize() == 0) { 487 GlobalVariables.getMessageMap().putError(String.format("%s.%s", 488 KRADConstants.NEW_DOCUMENT_NOTE_PROPERTY_NAME, 489 KRADConstants.NOTE_ATTACHMENT_FILE_PROPERTY_NAME), RiceKeyConstants.ERROR_UPLOADFILE_EMPTY, 490 attachmentFile.getOriginalFilename()); 491 } else { 492 if (newNote.getAttachment() != null) { 493 attachmentTypeCode = newNote.getAttachment().getAttachmentTypeCode(); 494 } 495 496 DocumentAuthorizer documentAuthorizer = getDocumentDictionaryService().getDocumentAuthorizer(document); 497 if (!documentAuthorizer.canAddNoteAttachment(document, attachmentTypeCode, 498 GlobalVariables.getUserSession().getPerson())) { 499 throw buildAuthorizationException("annotate", document); 500 } 501 502 try { 503 String attachmentType = null; 504 Attachment newAttachment = newNote.getAttachment(); 505 if (newAttachment != null) { 506 attachmentType = newAttachment.getAttachmentTypeCode(); 507 } 508 509 attachment = getAttachmentService().createAttachment(document.getNoteTarget(), 510 attachmentFile.getOriginalFilename(), attachmentFile.getContentType(), 511 (int) attachmentFile.getSize(), attachmentFile.getInputStream(), attachmentType); 512 } catch (IOException e) { 513 throw new RiceRuntimeException("Unable to store attachment", e); 514 } 515 } 516 } 517 518 Person kualiUser = GlobalVariables.getUserSession().getPerson(); 519 if (kualiUser == null) { 520 throw new IllegalStateException("Current UserSession has a null Person."); 521 } 522 523 newNote.setAuthorUniversalIdentifier(kualiUser.getPrincipalId()); 524 525 // validate the note 526 boolean rulePassed = KRADServiceLocatorWeb.getKualiRuleService().applyRules(new AddNoteEvent(document, 527 newNote)); 528 529 // if the rule evaluation passed, let's add the note 530 if (rulePassed) { 531 newNote.refresh(); 532 533 DocumentHeader documentHeader = document.getDocumentHeader(); 534 535 // adding the attachment after refresh gets called, since the attachment record doesn't get persisted 536 // until the note does (and therefore refresh doesn't have any attachment to autoload based on the id, nor does it 537 // autopopulate the id since the note hasn't been persisted yet) 538 if (attachment != null) { 539 newNote.addAttachment(attachment); 540 } 541 // Save the note if the document is already saved 542 if (!documentHeader.getWorkflowDocument().isInitiated() && StringUtils.isNotEmpty( 543 document.getNoteTarget().getObjectId()) && !(document instanceof MaintenanceDocument && NoteType 544 .BUSINESS_OBJECT.getCode().equals(newNote.getNoteTypeCode()))) { 545 546 getNoteService().save(newNote); 547 } 548 549 } 550 551 return addLine(uifForm, result, request, response); 552 } 553 554 /** 555 * Called by the delete note action for deleting a note. 556 * Calls the UifControllerBase.deleteLine method to handle 557 * generic actions. 558 */ 559 @RequestMapping(method = RequestMethod.POST, params = "methodToCall=deleteNote") 560 public ModelAndView deleteNote(@ModelAttribute("KualiForm") UifFormBase uifForm, BindingResult result, 561 HttpServletRequest request, HttpServletResponse response) { 562 563 String selectedLineIndex = uifForm.getActionParamaterValue("selectedLineIndex"); 564 Document document = ((DocumentFormBase) uifForm).getDocument(); 565 Note note = document.getNote(Integer.parseInt(selectedLineIndex)); 566 567 Attachment attachment = note.getAttachment(); 568 String attachmentTypeCode = null; 569 if (attachment != null) { 570 attachmentTypeCode = attachment.getAttachmentTypeCode(); 571 } 572 573 // check delete note authorization 574 Person user = GlobalVariables.getUserSession().getPerson(); 575 String authorUniversalIdentifier = note.getAuthorUniversalIdentifier(); 576 if (!getDocumentDictionaryService().getDocumentAuthorizer(document).canDeleteNoteAttachment(document, 577 attachmentTypeCode, authorUniversalIdentifier, user)) { 578 throw buildAuthorizationException("annotate", document); 579 } 580 581 if (attachment != null && attachment.isComplete()) { // only do this if the note has been persisted 582 //KFSMI-798 - refresh() changed to refreshNonUpdateableReferences() 583 //All references for the business object Attachment are auto-update="none", 584 //so refreshNonUpdateableReferences() should work the same as refresh() 585 if (note.getNoteIdentifier() 586 != null) { // KULRICE-2343 don't blow away note reference if the note wasn't persisted 587 attachment.refreshNonUpdateableReferences(); 588 } 589 getAttachmentService().deleteAttachmentContents(attachment); 590 } 591 // delete the note if the document is already saved 592 if (!document.getDocumentHeader().getWorkflowDocument().isInitiated()) { 593 getNoteService().deleteNote(note); 594 } 595 596 return deleteLine(uifForm, result, request, response); 597 } 598 599 /** 600 * Called by the download attachment action on a note. Method 601 * gets the attachment input stream from the AttachmentService 602 * and writes it to the request output stream. 603 */ 604 @RequestMapping(method = RequestMethod.POST, params = "methodToCall=downloadAttachment") 605 public ModelAndView downloadAttachment(@ModelAttribute("KualiForm") UifFormBase uifForm, BindingResult result, 606 HttpServletRequest request, 607 HttpServletResponse response) throws ServletRequestBindingException, FileNotFoundException, IOException { 608 // Get the attachment input stream 609 String selectedLineIndex = uifForm.getActionParamaterValue("selectedLineIndex"); 610 Note note = ((DocumentFormBase) uifForm).getDocument().getNote(Integer.parseInt(selectedLineIndex)); 611 Attachment attachment = note.getAttachment(); 612 InputStream is = getAttachmentService().retrieveAttachmentContents(attachment); 613 614 // Set the response headers 615 response.setContentType(attachment.getAttachmentMimeTypeCode()); 616 response.setContentLength(attachment.getAttachmentFileSize().intValue()); 617 response.setHeader("Expires", "0"); 618 response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0"); 619 response.setHeader("Pragma", "public"); 620 response.setHeader("Content-Disposition", 621 "attachment; filename=\"" + attachment.getAttachmentFileName() + "\""); 622 623 // Copy the input stream to the response 624 FileCopyUtils.copy(is, response.getOutputStream()); 625 return null; 626 } 627 628 /** 629 * Called by the cancel attachment action on a note. Method 630 * removes the attachment file from the form. 631 */ 632 @RequestMapping(method = RequestMethod.POST, params = "methodToCall=cancelAttachment") 633 public ModelAndView cancelAttachment(@ModelAttribute("KualiForm") UifFormBase uifForm, BindingResult result, 634 HttpServletRequest request, HttpServletResponse response) { 635 // Remove the attached file 636 uifForm.setAttachmentFile(null); 637 return getUIFModelAndView(uifForm); 638 } 639 640 /** 641 * Checks if the given value matches patterns that indicate sensitive data 642 * and if configured to give a warning for sensitive data will prompt the 643 * user to continue. 644 * 645 * @param form 646 * @param request 647 * @param response 648 * @param fieldName - name of field with value being checked 649 * @param fieldValue - value to check for sensitive data 650 * @param caller - method that should be called back from question 651 * @param context - additional context that needs to be passed back with the 652 * question response 653 * @return - view for spring to forward to, or null if processing should 654 * continue 655 * @throws Exception 656 */ 657 protected String checkAndWarnAboutSensitiveData(DocumentFormBase form, HttpServletRequest request, 658 HttpServletResponse response, String fieldName, String fieldValue, String caller, 659 String context) throws Exception { 660 661 String viewName = null; 662 Document document = form.getDocument(); 663 664 // TODO: need to move containsSensitiveDataPatternMatch to util class in krad 665// boolean containsSensitiveData = false; 666// //boolean containsSensitiveData = WebUtils.containsSensitiveDataPatternMatch(fieldValue); 667// 668// // check if warning is configured in which case we will prompt, or if 669// // not business rules will thrown an error 670// boolean warnForSensitiveData = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean( 671// KRADConstants.KRAD_NAMESPACE, ParameterConstants.ALL_COMPONENT, 672// KRADConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS_WARNING_IND); 673// 674// // determine if the question has been asked yet 675// Map<String, String> ticketContext = new HashMap<String, String>(); 676// ticketContext.put(KRADPropertyConstants.DOCUMENT_NUMBER, document.getDocumentNumber()); 677// ticketContext.put(KRADConstants.CALLING_METHOD, caller); 678// ticketContext.put(KRADPropertyConstants.NAME, fieldName); 679// 680// boolean questionAsked = GlobalVariables.getUserSession().hasMatchingSessionTicket( 681// KRADConstants.SENSITIVE_DATA_QUESTION_SESSION_TICKET, ticketContext); 682// 683// // start in logic for confirming the sensitive data 684// if (containsSensitiveData && warnForSensitiveData && !questionAsked) { 685// Object question = request.getParameter(KRADConstants.QUESTION_INST_ATTRIBUTE_NAME); 686// if (question == null || !KRADConstants.DOCUMENT_SENSITIVE_DATA_QUESTION.equals(question)) { 687// 688// // TODO not ready for question framework yet 689// /* 690// * // question hasn't been asked, prompt to continue return 691// * this.performQuestionWithoutInput(mapping, form, request, 692// * response, KRADConstants.DOCUMENT_SENSITIVE_DATA_QUESTION, 693// * getKualiConfigurationService() 694// * .getPropertyValueAsString(RiceKeyConstants 695// * .QUESTION_SENSITIVE_DATA_DOCUMENT), 696// * KRADConstants.CONFIRMATION_QUESTION, caller, context); 697// */ 698// viewName = "ask_user_questions"; 699// } else { 700// Object buttonClicked = request.getParameter(KRADConstants.QUESTION_CLICKED_BUTTON); 701// 702// // if no button clicked just reload the doc 703// if (ConfirmationQuestion.NO.equals(buttonClicked)) { 704// // TODO figure out what to return 705// viewName = "user_says_no"; 706// } 707// 708// // answered yes, create session ticket so we not to ask question 709// // again if there are further question requests 710// SessionTicket ticket = new SessionTicket(KRADConstants.SENSITIVE_DATA_QUESTION_SESSION_TICKET); 711// ticket.setTicketContext(ticketContext); 712// GlobalVariables.getUserSession().putSessionTicket(ticket); 713// } 714// } 715 716 // returning null will indicate processing should continue (no redirect) 717 return viewName; 718 } 719 720 /** 721 * Convenience method to combine the two lists of ad hoc recipients into one which should be done before 722 * calling any of the document service methods that expect a list of ad hoc recipients 723 * 724 * @param form - document form instance containing the ad hod lists 725 * @return List<AdHocRouteRecipient> combined ad hoc recipients 726 */ 727 protected List<AdHocRouteRecipient> combineAdHocRecipients(DocumentFormBase form) { 728 Document document = form.getDocument(); 729 730 List<AdHocRouteRecipient> adHocRecipients = new ArrayList<AdHocRouteRecipient>(); 731 adHocRecipients.addAll(document.getAdHocRoutePersons()); 732 adHocRecipients.addAll(document.getAdHocRouteWorkgroups()); 733 734 return adHocRecipients; 735 } 736 737 /** 738 * Convenience method for building authorization exceptions 739 * 740 * @param action - the action that was requested 741 * @param document - document instance the action was requested for 742 */ 743 protected DocumentAuthorizationException buildAuthorizationException(String action, Document document) { 744 return new DocumentAuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(), 745 action, document.getDocumentNumber()); 746 } 747 748 public BusinessObjectService getBusinessObjectService() { 749 if (this.businessObjectService == null) { 750 this.businessObjectService = KRADServiceLocator.getBusinessObjectService(); 751 } 752 return this.businessObjectService; 753 } 754 755 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 756 this.businessObjectService = businessObjectService; 757 } 758 759 public DataDictionaryService getDataDictionaryService() { 760 if (this.dataDictionaryService == null) { 761 this.dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); 762 } 763 return this.dataDictionaryService; 764 } 765 766 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 767 this.dataDictionaryService = dataDictionaryService; 768 } 769 770 public DocumentService getDocumentService() { 771 if (this.documentService == null) { 772 this.documentService = KRADServiceLocatorWeb.getDocumentService(); 773 } 774 return this.documentService; 775 } 776 777 public void setDocumentService(DocumentService documentService) { 778 this.documentService = documentService; 779 } 780 781 public DocumentDictionaryService getDocumentDictionaryService() { 782 if (this.documentDictionaryService == null) { 783 this.documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService(); 784 } 785 return this.documentDictionaryService; 786 } 787 788 public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) { 789 this.documentDictionaryService = documentDictionaryService; 790 } 791 792 public AttachmentService getAttachmentService() { 793 if (attachmentService == null) { 794 attachmentService = KRADServiceLocator.getAttachmentService(); 795 } 796 return this.attachmentService; 797 } 798 799 public NoteService getNoteService() { 800 if (noteService == null) { 801 noteService = KRADServiceLocator.getNoteService(); 802 } 803 804 return this.noteService; 805 } 806 807 public ConfigurationService getConfigurationService() { 808 return KRADServiceLocator.getKualiConfigurationService(); 809 } 810 811}