001 /* 002 * Copyright 2006-2011 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 */ 016 017 package org.kuali.rice.kns.web.struts.action; 018 019 import org.apache.commons.lang.ArrayUtils; 020 import org.apache.commons.lang.StringUtils; 021 import org.apache.ojb.broker.OptimisticLockException; 022 import org.apache.struts.action.ActionForm; 023 import org.apache.struts.action.ActionForward; 024 import org.apache.struts.action.ActionMapping; 025 import org.apache.struts.upload.FormFile; 026 import org.kuali.rice.core.api.config.property.ConfigurationService; 027 import org.kuali.rice.core.framework.parameter.ParameterConstants; 028 import org.kuali.rice.core.framework.parameter.ParameterService; 029 import org.kuali.rice.core.framework.services.CoreFrameworkServiceLocator; 030 import org.kuali.rice.core.util.ConcreteKeyValue; 031 import org.kuali.rice.core.util.KeyValue; 032 import org.kuali.rice.core.util.RiceConstants; 033 import org.kuali.rice.core.util.RiceKeyConstants; 034 import org.kuali.rice.kew.exception.WorkflowException; 035 import org.kuali.rice.kew.util.KEWConstants; 036 import org.kuali.rice.kim.api.group.Group; 037 import org.kuali.rice.kim.api.services.IdentityManagementService; 038 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 039 import org.kuali.rice.kim.bo.Person; 040 import org.kuali.rice.kim.util.KimConstants; 041 import org.kuali.rice.kns.UserSession; 042 import org.kuali.rice.kns.bo.AdHocRoutePerson; 043 import org.kuali.rice.kns.bo.AdHocRouteRecipient; 044 import org.kuali.rice.kns.bo.AdHocRouteWorkgroup; 045 import org.kuali.rice.kns.bo.Attachment; 046 import org.kuali.rice.kns.bo.DocumentHeader; 047 import org.kuali.rice.kns.bo.Note; 048 import org.kuali.rice.kns.bo.PersistableBusinessObject; 049 import org.kuali.rice.kns.datadictionary.DataDictionary; 050 import org.kuali.rice.kns.datadictionary.DocumentEntry; 051 import org.kuali.rice.kns.document.Document; 052 import org.kuali.rice.kns.document.MaintenanceDocument; 053 import org.kuali.rice.kns.document.authorization.DocumentAuthorizer; 054 import org.kuali.rice.kns.document.authorization.DocumentAuthorizerBase; 055 import org.kuali.rice.kns.document.authorization.DocumentPresentationController; 056 import org.kuali.rice.kns.document.authorization.PessimisticLock; 057 import org.kuali.rice.kns.exception.AuthorizationException; 058 import org.kuali.rice.kns.exception.DocumentAuthorizationException; 059 import org.kuali.rice.kns.exception.UnknownDocumentIdException; 060 import org.kuali.rice.kns.question.ConfirmationQuestion; 061 import org.kuali.rice.kns.rule.PromptBeforeValidation; 062 import org.kuali.rice.kns.rule.event.AddAdHocRoutePersonEvent; 063 import org.kuali.rice.kns.rule.event.AddAdHocRouteWorkgroupEvent; 064 import org.kuali.rice.kns.rule.event.AddNoteEvent; 065 import org.kuali.rice.kns.rule.event.PromptBeforeValidationEvent; 066 import org.kuali.rice.kns.rule.event.SendAdHocRequestsEvent; 067 import org.kuali.rice.kns.service.AttachmentService; 068 import org.kuali.rice.kns.service.BusinessObjectAuthorizationService; 069 import org.kuali.rice.kns.service.BusinessObjectMetaDataService; 070 import org.kuali.rice.kns.service.BusinessObjectService; 071 import org.kuali.rice.kns.service.DataDictionaryService; 072 import org.kuali.rice.kns.service.DocumentHelperService; 073 import org.kuali.rice.kns.service.DocumentService; 074 import org.kuali.rice.kns.service.KNSServiceLocator; 075 import org.kuali.rice.kns.service.KNSServiceLocatorWeb; 076 import org.kuali.rice.kns.service.KualiRuleService; 077 import org.kuali.rice.kns.service.NoteService; 078 import org.kuali.rice.kns.service.PessimisticLockService; 079 import org.kuali.rice.kns.util.GlobalVariables; 080 import org.kuali.rice.kns.util.KNSConstants; 081 import org.kuali.rice.kns.util.KNSPropertyConstants; 082 import org.kuali.rice.kns.util.NoteType; 083 import org.kuali.rice.kns.util.ObjectUtils; 084 import org.kuali.rice.kns.util.SessionTicket; 085 import org.kuali.rice.kns.util.UrlFactory; 086 import org.kuali.rice.kns.util.WebUtils; 087 import org.kuali.rice.kns.web.struts.form.BlankFormFile; 088 import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase; 089 import org.kuali.rice.kns.web.struts.form.KualiForm; 090 import org.kuali.rice.kns.web.struts.form.KualiMaintenanceForm; 091 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument; 092 import org.springmodules.orm.ojb.OjbOperationException; 093 094 import javax.persistence.EntityManagerFactory; 095 import javax.servlet.http.HttpServletRequest; 096 import javax.servlet.http.HttpServletResponse; 097 import java.io.ByteArrayOutputStream; 098 import java.io.IOException; 099 import java.util.ArrayList; 100 import java.util.Enumeration; 101 import java.util.HashMap; 102 import java.util.Iterator; 103 import java.util.List; 104 import java.util.Map; 105 import java.util.Properties; 106 import java.util.Set; 107 108 109 /** 110 * This class handles all of the document handling related actions in terms of passing them from here at a central point to the 111 * distributed transactions that actually implement document handling. 112 */ 113 public class KualiDocumentActionBase extends KualiAction { 114 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiDocumentActionBase.class); 115 116 // COMMAND constants which cause docHandler to load an existing document instead of creating a new one 117 protected static final String[] DOCUMENT_LOAD_COMMANDS = { 118 KEWConstants.ACTIONLIST_COMMAND, 119 KEWConstants.DOCSEARCH_COMMAND, 120 KEWConstants.SUPERUSER_COMMAND, 121 KEWConstants.HELPDESK_ACTIONLIST_COMMAND}; 122 123 private DataDictionaryService dataDictionaryService; 124 private DocumentHelperService documentHelperService; 125 private DocumentService documentService; 126 private ConfigurationService kualiConfigurationService; 127 private ParameterService parameterService; 128 private PessimisticLockService pessimisticLockService; 129 private KualiRuleService kualiRuleService; 130 private IdentityManagementService identityManagementService; 131 private AttachmentService attachmentService; 132 private NoteService noteService; 133 private BusinessObjectAuthorizationService businessObjectAuthorizationService; 134 private BusinessObjectService businessObjectService; 135 private BusinessObjectMetaDataService businessObjectMetaDataService; 136 private EntityManagerFactory entityManagerFactory; 137 138 @Override 139 protected void checkAuthorization(ActionForm form, String methodToCall) throws AuthorizationException { 140 if (!(form instanceof KualiDocumentFormBase)) { 141 super.checkAuthorization(form, methodToCall); 142 } 143 } 144 145 /** 146 * Entry point to all actions. 147 * <p/> 148 * NOTE: No need to hook into execute for handling framwork setup anymore. Just implement the methodToCall for the framework 149 * setup, Constants.METHOD_REQUEST_PARAMETER will contain the full parameter, which can be sub stringed for getting framework 150 * parameters. 151 * 152 * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, 153 * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 154 */ 155 @Override 156 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 157 ActionForward returnForward = mapping.findForward(RiceConstants.MAPPING_BASIC); 158 159 // if found methodToCall, pass control to that method 160 try { 161 returnForward = super.execute(mapping, form, request, response); 162 } catch (OjbOperationException e) { 163 // special handling for OptimisticLockExceptions 164 OjbOperationException ooe = e; 165 166 Throwable cause = ooe.getCause(); 167 if (cause instanceof OptimisticLockException) { 168 OptimisticLockException ole = (OptimisticLockException) cause; 169 GlobalVariables.getMessageMap().putError(KNSConstants.DOCUMENT_ERRORS, RiceKeyConstants.ERROR_OPTIMISTIC_LOCK); 170 logOjbOptimisticLockException(ole); 171 } else { 172 // if exceptions are from 'save' 173 throw e; 174 } 175 } finally { 176 if (form instanceof KualiDocumentFormBase) { 177 ((KualiDocumentFormBase) form).setMessageMapFromPreviousRequest(GlobalVariables.getMessageMap()); 178 } 179 } 180 181 if (form instanceof KualiDocumentFormBase 182 && ((KualiDocumentFormBase) form).isHasWorkflowDocument()) { 183 KualiDocumentFormBase formBase = (KualiDocumentFormBase) form; 184 Document document = formBase.getDocument(); 185 186 //KULRICE-2210 fix location of document header population 187 KualiWorkflowDocument workflowDocument = formBase.getDocument().getDocumentHeader().getWorkflowDocument(); 188 formBase.populateHeaderFields(workflowDocument); 189 formBase.setDocId(document.getDocumentNumber()); 190 //End of KULRICE-2210 fix 191 192 // check to see if document is a pessimistic lock document 193 if (isFormRepresentingLockObject(formBase)) { 194 // form represents a document using the BO class PessimisticLock so we need to skip the authorizations in the next logic check 195 if (LOG.isDebugEnabled()) { 196 LOG.debug("Form " + formBase + " represents a PessimisticLock BO object"); 197 } 198 } else { 199 // populates authorization-related fields in KualiDocumentFormBase instances, which are derived from 200 // information which is contained in the form but which may be unavailable until this point 201 //DocumentAuthorizer documentAuthorizer = KNSServiceLocatorInternal.getDocumentAuthorizationService().getDocumentAuthorizer(document); 202 //formBase.populateAuthorizationFields(documentAuthorizer); 203 populateAuthorizationFields(formBase); 204 populateAdHocActionRequestCodes(formBase); 205 206 //set the formBase into userSession if the document is a session document 207 UserSession userSession = (UserSession) request.getSession().getAttribute(KNSConstants.USER_SESSION_KEY); 208 209 if (WebUtils.isDocumentSession(document, formBase)) { 210 String formKey = formBase.getFormKey(); 211 if (StringUtils.isBlank(formBase.getFormKey()) || userSession.retrieveObject(formBase.getFormKey()) == null) { 212 // generate doc form key here if it does not exist 213 formKey = GlobalVariables.getUserSession().addObjectWithGeneratedKey(form); 214 formBase.setFormKey(formKey); 215 } 216 } 217 218 219 // below used by KualiHttpSessionListener to handle lock expiration 220 request.getSession().setAttribute(KNSConstants.DOCUMENT_HTTP_SESSION_KEY, document.getDocumentNumber()); 221 // set returnToActionList flag, if needed 222 if ("displayActionListView".equals(formBase.getCommand())) { 223 formBase.setReturnToActionList(true); 224 } 225 226 String attachmentEnabled = 227 getKualiConfigurationService().getPropertyString( 228 KNSConstants.NOTE_ATTACHMENT_ENABLED); 229 // Override the document entry 230 if (attachmentEnabled != null) { 231 // This is a hack for KULRICE-1602 since the document entry is modified by a 232 // global configuration that overrides the document templates without some sort 233 // of rules or control 234 //DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary(); 235 DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary(); 236 237 DocumentEntry entry = dataDictionary.getDocumentEntry(document.getClass().getName()); 238 entry.setAllowsNoteAttachments(Boolean.parseBoolean(attachmentEnabled)); 239 } 240 //the request attribute will be used in KualiRequestProcess#processActionPerform 241 if (exitingDocument()) { 242 request.setAttribute(KNSConstants.EXITING_DOCUMENT, Boolean.TRUE); 243 } 244 245 // pessimistic locking 246 String methodCalledViaDispatch = (String) GlobalVariables.getUserSession().retrieveObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_OBJECT_KEY); 247 if ((StringUtils.isNotBlank(methodCalledViaDispatch)) && (exitingDocument())) { 248 GlobalVariables.getUserSession().removeObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_OBJECT_KEY); 249 attemptLockRelease(document, methodCalledViaDispatch); 250 } 251 setupPessimisticLockMessages(document, request); 252 if (!document.getPessimisticLocks().isEmpty()) { 253 String warningMinutes = getParameterService().getParameterValueAsString(KNSConstants.KNS_NAMESPACE, KNSConstants.DetailTypes.DOCUMENT_DETAIL_TYPE, KNSConstants.SESSION_TIMEOUT_WARNING_MESSAGE_TIME_PARM_NM); 254 request.setAttribute(KNSConstants.SESSION_TIMEOUT_WARNING_MINUTES, warningMinutes); 255 request.setAttribute(KNSConstants.SESSION_TIMEOUT_WARNING_MILLISECONDS, (request.getSession().getMaxInactiveInterval() - (Integer.valueOf(warningMinutes) * 60)) * 1000); 256 } 257 } 258 } 259 260 return returnForward; 261 } 262 263 protected boolean isFormRepresentingLockObject(KualiDocumentFormBase form) throws Exception { 264 if (form instanceof KualiMaintenanceForm) { 265 KualiMaintenanceForm maintForm = (KualiMaintenanceForm) form; 266 if (ObjectUtils.isNotNull(maintForm.getBusinessObjectClassName())) { 267 return PessimisticLock.class.isAssignableFrom(Class.forName(((KualiMaintenanceForm) form).getBusinessObjectClassName())); 268 } 269 } 270 return false; 271 } 272 273 protected void attemptLockRelease(Document document, String methodToCall) { 274 if ((document != null) && (!document.getPessimisticLocks().isEmpty())) { 275 releaseLocks(document, methodToCall); 276 // refresh pessimistic locks in case custom add/remove changes were made 277 //document.refreshPessimisticLocks(); 278 } 279 } 280 281 protected void releaseLocks(Document document, String methodToCall) { 282 // first check if the method to call is listed as required lock clearing 283 if (document.getLockClearningMethodNames().contains(methodToCall)) { 284 // find all locks for the current user and remove them 285 getPessimisticLockService().releaseAllLocksForUser(document.getPessimisticLocks(), GlobalVariables.getUserSession().getPerson()); 286 } 287 } 288 289 protected void setupPessimisticLockMessages(Document document, HttpServletRequest request) { 290 List<String> lockMessages = new ArrayList<String>(); 291 for (PessimisticLock lock : document.getPessimisticLocks()) { 292 // if lock is owned by current user, do not display message for it 293 if (!lock.isOwnedByUser(GlobalVariables.getUserSession().getPerson())) { 294 lockMessages.add(generatePessimisticLockMessage(lock)); 295 } 296 } 297 request.setAttribute(KNSConstants.PESSIMISTIC_LOCK_MESSAGES, lockMessages); 298 } 299 300 protected String generatePessimisticLockMessage(PessimisticLock lock) { 301 String descriptor = (lock.getLockDescriptor() != null) ? lock.getLockDescriptor() : ""; 302 // TODO: this should be pulled into a properties file 303 return "This document currently has a " + descriptor + " lock owned by " + lock.getOwnedByUser().getName() + " as of " + RiceConstants.getDefaultTimeFormat().format(lock.getGeneratedTimestamp()) + " on " + RiceConstants.getDefaultDateFormat().format(lock.getGeneratedTimestamp()); 304 } 305 306 // private void saveMessages(HttpServletRequest request) { 307 // if (!GlobalVariables.getMessageList().isEmpty()) { 308 // request.setAttribute(KNSConstants.GLOBAL_MESSAGES, GlobalVariables.getMessageList()); 309 // } 310 // } 311 312 /** 313 * This method may be used to funnel all document handling through, we could do useful things like log and record various 314 * openings and status Additionally it may be nice to have a single dispatcher that can know how to dispatch to a redirect url 315 * for document specific handling but we may not need that as all we should need is the document to be able to load itself based 316 * on document id and then which actionforward or redirect is pertinent for the document type. 317 * 318 * @param mapping 319 * @param form 320 * @param request 321 * @param response 322 * @return ActionForward 323 * @throws Exception 324 */ 325 public ActionForward docHandler(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 326 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 327 String command = kualiDocumentFormBase.getCommand(); 328 329 // in all of the following cases we want to load the document 330 if (ArrayUtils.contains(DOCUMENT_LOAD_COMMANDS, command) && kualiDocumentFormBase.getDocId() != null) { 331 loadDocument(kualiDocumentFormBase); 332 } else if (KEWConstants.INITIATE_COMMAND.equals(command)) { 333 createDocument(kualiDocumentFormBase); 334 } else { 335 LOG.error("docHandler called with invalid parameters"); 336 throw new IllegalStateException("docHandler called with invalid parameters"); 337 } 338 339 // attach any extra JS from the data dictionary 340 if (LOG.isDebugEnabled()) { 341 LOG.debug("kualiDocumentFormBase.getAdditionalScriptFiles(): " + kualiDocumentFormBase.getAdditionalScriptFiles()); 342 } 343 if (kualiDocumentFormBase.getAdditionalScriptFiles().isEmpty()) { 344 DocumentEntry docEntry = getDataDictionaryService().getDataDictionary().getDocumentEntry(kualiDocumentFormBase.getDocument().getDocumentHeader().getWorkflowDocument().getDocumentType()); 345 kualiDocumentFormBase.getAdditionalScriptFiles().addAll(docEntry.getWebScriptFiles()); 346 } 347 if (KEWConstants.SUPERUSER_COMMAND.equalsIgnoreCase(command)) { 348 kualiDocumentFormBase.setSuppressAllButtons(true); 349 } 350 return mapping.findForward(RiceConstants.MAPPING_BASIC); 351 } 352 353 /** 354 * This method loads the document by its provided document header id. This has been abstracted out so that it can be overridden 355 * in children if the need arises. 356 * 357 * @param kualiDocumentFormBase 358 * @throws WorkflowException 359 */ 360 protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException { 361 String docId = kualiDocumentFormBase.getDocId(); 362 Document doc = null; 363 doc = getDocumentService().getByDocumentHeaderId(docId); 364 if (doc == null) { 365 throw new UnknownDocumentIdException("Document no longer exists. It may have been cancelled before being saved."); 366 } 367 KualiWorkflowDocument workflowDocument = doc.getDocumentHeader().getWorkflowDocument(); 368 if (!getDocumentHelperService().getDocumentAuthorizer(doc).canOpen(doc, GlobalVariables.getUserSession().getPerson())) { 369 throw buildAuthorizationException("open", doc); 370 } 371 // re-retrieve the document using the current user's session - remove the system user from the WorkflowDcument object 372 if (workflowDocument != doc.getDocumentHeader().getWorkflowDocument()) { 373 LOG.warn("Workflow document changed via canOpen check"); 374 doc.getDocumentHeader().setWorkflowDocument(workflowDocument); 375 } 376 kualiDocumentFormBase.setDocument(doc); 377 KualiWorkflowDocument workflowDoc = doc.getDocumentHeader().getWorkflowDocument(); 378 kualiDocumentFormBase.setDocTypeName(workflowDoc.getDocumentType()); 379 // KualiDocumentFormBase.populate() needs this updated in the session 380 KNSServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(), workflowDoc); 381 } 382 383 384 /** 385 * This method creates a new document of the type specified by the docTypeName property of the given form. This has been 386 * abstracted out so that it can be overridden in children if the need arises. 387 * 388 * @param kualiDocumentFormBase 389 * @throws WorkflowException 390 */ 391 protected void createDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException { 392 Document doc = getDocumentService().getNewDocument(kualiDocumentFormBase.getDocTypeName()); 393 394 kualiDocumentFormBase.setDocument(doc); 395 kualiDocumentFormBase.setDocTypeName(doc.getDocumentHeader().getWorkflowDocument().getDocumentType()); 396 } 397 398 /** 399 * This method will insert the new ad hoc person from the from into the list of ad hoc person recipients, put a new new record 400 * in place and return like normal. 401 * 402 * @param mapping 403 * @param form 404 * @param request 405 * @param response 406 * @return ActionForward 407 * @throws Exception 408 */ 409 public ActionForward insertAdHocRoutePerson(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 410 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 411 Document document = kualiDocumentFormBase.getDocument(); 412 413 414 // check authorization for adding ad hoc route person 415 DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(document); 416 if (!documentAuthorizer.canSendAdHocRequests(document, kualiDocumentFormBase.getNewAdHocRoutePerson().getActionRequested(), GlobalVariables.getUserSession().getPerson())) { 417 throw buildAuthorizationException("ad-hoc route", document); 418 } 419 420 // check business rules 421 boolean rulePassed = getKualiRuleService().applyRules(new AddAdHocRoutePersonEvent(document, kualiDocumentFormBase.getNewAdHocRoutePerson())); 422 423 // if the rule evaluation passed, let's add the ad hoc route person 424 if (rulePassed) { 425 // uppercase userid for consistency 426 // kualiDocumentFormBase.getNewAdHocRoutePerson().setId(StringUtils.upperCase(kualiDocumentFormBase.getNewAdHocRoutePerson().getId())); 427 kualiDocumentFormBase.getNewAdHocRoutePerson().setId(kualiDocumentFormBase.getNewAdHocRoutePerson().getId()); 428 kualiDocumentFormBase.getAdHocRoutePersons().add(kualiDocumentFormBase.getNewAdHocRoutePerson()); 429 AdHocRoutePerson person = new AdHocRoutePerson(); 430 kualiDocumentFormBase.setNewAdHocRoutePerson(person); 431 } 432 433 return mapping.findForward(RiceConstants.MAPPING_BASIC); 434 } 435 436 /** 437 * This method will delete one of the ad hoc persons from the list of ad hoc persons to route to based on the line number of the 438 * delete button that was clicked. then it will return to the form. 439 * 440 * @param mapping 441 * @param form 442 * @param request 443 * @param response 444 * @return ActionForward 445 * @throws Exception 446 */ 447 public ActionForward deleteAdHocRoutePerson(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 448 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 449 450 451 kualiDocumentFormBase.getAdHocRoutePersons().remove(this.getLineToDelete(request)); 452 return mapping.findForward(RiceConstants.MAPPING_BASIC); 453 } 454 455 /** 456 * This method will insert the new ad hoc workgroup into the list of ad hoc workgroup recipients put a nuew record in place and 457 * then return like normal. 458 * 459 * @param mapping 460 * @param form 461 * @param request 462 * @param response 463 * @return ActionForward 464 * @throws Exception 465 */ 466 public ActionForward insertAdHocRouteWorkgroup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 467 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 468 Document document = kualiDocumentFormBase.getDocument(); 469 470 // check authorization for add ad hoc route workgroup 471 DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(document); 472 if (!documentAuthorizer.canSendAdHocRequests(document, kualiDocumentFormBase.getNewAdHocRouteWorkgroup().getActionRequested(), GlobalVariables.getUserSession().getPerson())) { 473 throw buildAuthorizationException("ad-hoc route", document); 474 } 475 476 // check business rules 477 boolean rulePassed = getKualiRuleService().applyRules(new AddAdHocRouteWorkgroupEvent(document, kualiDocumentFormBase.getNewAdHocRouteWorkgroup())); 478 479 // if the rule evaluation passed, let's add the ad hoc route workgroup 480 if (rulePassed) { 481 //fill id if not already filled 482 AdHocRouteWorkgroup newWorkgroup = kualiDocumentFormBase.getNewAdHocRouteWorkgroup(); 483 if (newWorkgroup.getId() == null) { 484 newWorkgroup.setId(KimApiServiceLocator.getIdentityManagementService().getGroupByName(newWorkgroup.getRecipientNamespaceCode(), newWorkgroup.getRecipientName()).getId()); 485 } 486 kualiDocumentFormBase.getAdHocRouteWorkgroups().add(newWorkgroup); 487 AdHocRouteWorkgroup workgroup = new AdHocRouteWorkgroup(); 488 kualiDocumentFormBase.setNewAdHocRouteWorkgroup(workgroup); 489 } 490 491 return mapping.findForward(RiceConstants.MAPPING_BASIC); 492 } 493 494 /** 495 * This method will delete one of the ad hoc workgroups from the list of ad hoc workgroups to route to based on the line number 496 * of the delete button that was clicked. then it will return 497 * 498 * @param mapping 499 * @param form 500 * @param request 501 * @param response 502 * @return ActionForward 503 * @throws Exception 504 */ 505 public ActionForward deleteAdHocRouteWorkgroup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 506 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 507 508 kualiDocumentFormBase.getAdHocRouteWorkgroups().remove(this.getLineToDelete(request)); 509 return mapping.findForward(RiceConstants.MAPPING_BASIC); 510 } 511 512 public ActionForward sendAdHocRequests(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 513 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 514 Document document = kualiDocumentFormBase.getDocument(); 515 516 boolean rulePassed = getKualiRuleService().applyRules(new SendAdHocRequestsEvent(document)); 517 518 if (rulePassed) { 519 getDocumentService().sendAdHocRequests(document, kualiDocumentFormBase.getAnnotation(), combineAdHocRecipients(kualiDocumentFormBase)); 520 GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_SEND_AD_HOC_REQUESTS_SUCCESSFUL); 521 } 522 523 return mapping.findForward(RiceConstants.MAPPING_BASIC); 524 } 525 526 /** 527 * This method will reload the document. 528 * 529 * @param mapping 530 * @param form 531 * @param request 532 * @param response 533 * @return ActionForward 534 * @throws Exception 535 */ 536 public ActionForward reload(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 537 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 538 Document document = kualiDocumentFormBase.getDocument(); 539 540 // prepare for the reload action - set doc id and command 541 kualiDocumentFormBase.setDocId(document.getDocumentNumber()); 542 kualiDocumentFormBase.setCommand(DOCUMENT_LOAD_COMMANDS[1]); 543 544 // forward off to the doc handler 545 ActionForward actionForward = docHandler(mapping, form, request, response); 546 547 GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_RELOADED); 548 // TODO: remove this when further testing passed 549 // if (form instanceof KualiDocumentFormBase) { 550 // UserSession userSession = (UserSession) request.getSession().getAttribute(RiceConstants.USER_SESSION_KEY); 551 // // force to recreate formkey in execute method 552 // if (document instanceof SessionDocument && userSession.retrieveObject(kualiDocumentFormBase.getFormKey()) != null) { 553 // userSession.removeObject(kualiDocumentFormBase.getFormKey());; 554 // } 555 // } 556 557 return actionForward; 558 } 559 560 /** 561 * This method will save the document, which will then be available via the action list for the person who saved the document. 562 * 563 * @param mapping 564 * @param form 565 * @param request 566 * @param response 567 * @return ActionForward 568 * @throws Exception 569 */ 570 public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 571 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 572 doProcessingAfterPost(kualiDocumentFormBase, request); 573 //get any possible changes to to adHocWorkgroups 574 refreshAdHocRoutingWorkgroupLookups(request, kualiDocumentFormBase); 575 Document document = kualiDocumentFormBase.getDocument(); 576 577 ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KNSPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "save", ""); 578 if (forward != null) { 579 return forward; 580 } 581 582 // save in workflow 583 getDocumentService().saveDocument(document); 584 585 GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_SAVED); 586 kualiDocumentFormBase.setAnnotation(""); 587 588 // TODO: remove this when further testing passed 589 // if (form instanceof KualiDocumentFormBase) { 590 // UserSession userSession = (UserSession) request.getSession().getAttribute(RiceConstants.USER_SESSION_KEY); 591 // // force to recreate formkey in execute method 592 // if (document instanceof SessionDocument && userSession.retrieveObject(kualiDocumentFormBase.getFormKey()) != null) { 593 // userSession.removeObject(kualiDocumentFormBase.getFormKey());; 594 // } 595 // } 596 597 return mapping.findForward(RiceConstants.MAPPING_BASIC); 598 } 599 600 /** 601 * Checks if the given value matches patterns that indicate sensitive data and if configured to give a warning for sensitive data will 602 * prompt the user to continue 603 * 604 * @param mapping 605 * @param form 606 * @param request 607 * @param response 608 * @param fieldName - name of field with value being checked 609 * @param fieldValue - value to check for sensitive data 610 * @param caller - method that should be called back from question 611 * @param context - additional context that needs to be passed back with the question response 612 * @return ActionForward which contains the question forward, or basic forward if user select no to prompt, otherwise will return null 613 * to indicate processing should continue 614 * @throws Exception 615 */ 616 protected ActionForward checkAndWarnAboutSensitiveData(ActionMapping mapping, ActionForm form, 617 HttpServletRequest request, HttpServletResponse response, String fieldName, String fieldValue, String caller, String context) 618 throws Exception { 619 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 620 Document document = kualiDocumentFormBase.getDocument(); 621 622 boolean containsSensitiveData = WebUtils.containsSensitiveDataPatternMatch(fieldValue); 623 624 // check if warning is configured in which case we will prompt, or if not business rules will thrown an error 625 boolean warnForSensitiveData = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean( 626 KNSConstants.KNS_NAMESPACE, ParameterConstants.ALL_COMPONENT, 627 KNSConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS_WARNING_IND); 628 629 // determine if the question has been asked yet 630 Map<String, String> ticketContext = new HashMap<String, String>(); 631 ticketContext.put(KNSPropertyConstants.DOCUMENT_NUMBER, document.getDocumentNumber()); 632 ticketContext.put(KNSConstants.CALLING_METHOD, caller); 633 ticketContext.put(KNSPropertyConstants.NAME, fieldName); 634 635 boolean questionAsked = GlobalVariables.getUserSession().hasMatchingSessionTicket( 636 KNSConstants.SENSITIVE_DATA_QUESTION_SESSION_TICKET, ticketContext); 637 638 // start in logic for confirming the sensitive data 639 if (containsSensitiveData && warnForSensitiveData && !questionAsked) { 640 Object question = request.getParameter(KNSConstants.QUESTION_INST_ATTRIBUTE_NAME); 641 if (question == null || !KNSConstants.DOCUMENT_SENSITIVE_DATA_QUESTION.equals(question)) { 642 643 // question hasn't been asked, prompt to continue 644 return this.performQuestionWithoutInput(mapping, form, request, response, 645 KNSConstants.DOCUMENT_SENSITIVE_DATA_QUESTION, getKualiConfigurationService() 646 .getPropertyString(RiceKeyConstants.QUESTION_SENSITIVE_DATA_DOCUMENT), 647 KNSConstants.CONFIRMATION_QUESTION, caller, context); 648 } 649 650 Object buttonClicked = request.getParameter(KNSConstants.QUESTION_CLICKED_BUTTON); 651 if (question != null && KNSConstants.DOCUMENT_SENSITIVE_DATA_QUESTION.equals(question)) { 652 // if no button clicked just reload the doc 653 if (ConfirmationQuestion.NO.equals(buttonClicked)) { 654 655 return mapping.findForward(RiceConstants.MAPPING_BASIC); 656 } 657 658 // answered yes, create session ticket so we not to ask question again if there are further question requests 659 SessionTicket ticket = new SessionTicket(KNSConstants.SENSITIVE_DATA_QUESTION_SESSION_TICKET); 660 ticket.setTicketContext(ticketContext); 661 GlobalVariables.getUserSession().putSessionTicket(ticket); 662 } 663 } 664 665 // return null to indicate processing should continue (no redirect) 666 return null; 667 } 668 669 /** 670 * This method will verify that the form is representing a {@link PessimisticLock} object and delete it if possible 671 * 672 * @param mapping 673 * @param form 674 * @param request 675 * @param response 676 * @return ActionForward 677 * @throws Exception 678 */ 679 public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 680 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 681 if (isFormRepresentingLockObject(kualiDocumentFormBase)) { 682 String idValue = request.getParameter(KNSPropertyConstants.ID); 683 getPessimisticLockService().delete(idValue); 684 return returnToSender(request, mapping, kualiDocumentFormBase); 685 } 686 throw buildAuthorizationException(KNSConstants.DELETE_METHOD, kualiDocumentFormBase.getDocument()); 687 } 688 689 /** 690 * route the document using the document service 691 * 692 * @param mapping 693 * @param form 694 * @param request 695 * @param response 696 * @return ActionForward 697 * @throws Exception 698 */ 699 public ActionForward performRouteReport(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 700 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 701 702 kualiDocumentFormBase.setDerivedValuesOnForm(request); 703 ActionForward preRulesForward = promptBeforeValidation(mapping, form, request, response); 704 if (preRulesForward != null) { 705 return preRulesForward; 706 } 707 708 Document document = kualiDocumentFormBase.getDocument(); 709 // check authorization for reloading document 710 //DocumentActionFlags flags = getDocumentActionFlags(document); 711 if (!kualiDocumentFormBase.getDocumentActions().containsKey(KNSConstants.KUALI_ACTION_PERFORM_ROUTE_REPORT)) { 712 throw buildAuthorizationException("perform route report", document); 713 } 714 715 String backUrlBase = getReturnLocation(request, mapping); 716 String globalVariableFormKey = GlobalVariables.getUserSession().addObjectWithGeneratedKey(form); 717 // setup back form variables 718 request.setAttribute("backUrlBase", backUrlBase); 719 List<KeyValue> backFormParameters = new ArrayList<KeyValue>(); 720 backFormParameters.add(new ConcreteKeyValue(KNSConstants.DISPATCH_REQUEST_PARAMETER, KNSConstants.RETURN_METHOD_TO_CALL)); 721 backFormParameters.add(new ConcreteKeyValue(KNSConstants.DOC_FORM_KEY, globalVariableFormKey)); 722 request.setAttribute("backFormHiddenVariables", backFormParameters); 723 724 // setup route report form variables 725 request.setAttribute("workflowRouteReportUrl", getKualiConfigurationService().getPropertyString(KNSConstants.WORKFLOW_URL_KEY) + "/" + KEWConstants.DOCUMENT_ROUTING_REPORT_PAGE); 726 List<KeyValue> generalRouteReportFormParameters = new ArrayList<KeyValue>(); 727 generalRouteReportFormParameters.add(new ConcreteKeyValue(KEWConstants.INITIATOR_ID_ATTRIBUTE_NAME, document.getDocumentHeader().getWorkflowDocument().getRouteHeader().getInitiatorPrincipalId())); 728 generalRouteReportFormParameters.add(new ConcreteKeyValue(KEWConstants.DOCUMENT_TYPE_NAME_ATTRIBUTE_NAME, document.getDocumentHeader().getWorkflowDocument().getDocumentType())); 729 // prepareForRouteReport() method should populate document header workflow document application content xml 730 String xml = document.getXmlForRouteReport(); 731 if (LOG.isDebugEnabled()) { 732 LOG.debug("XML being used for Routing Report is: " + xml); 733 } 734 generalRouteReportFormParameters.add(new ConcreteKeyValue(KEWConstants.DOCUMENT_CONTENT_ATTRIBUTE_NAME, xml)); 735 736 // set up the variables for the form if java script is working (includes a close button variable and no back url) 737 List<KeyValue> javaScriptFormParameters = new ArrayList<KeyValue>(); 738 javaScriptFormParameters.addAll(generalRouteReportFormParameters); 739 javaScriptFormParameters.add(new ConcreteKeyValue(KEWConstants.DISPLAY_CLOSE_BUTTON_ATTRIBUTE_NAME, KEWConstants.DISPLAY_CLOSE_BUTTON_TRUE_VALUE)); 740 request.setAttribute("javaScriptFormVariables", javaScriptFormParameters); 741 742 // set up the variables for the form if java script is NOT working (includes a back url but no close button) 743 List<KeyValue> noJavaScriptFormParameters = new ArrayList<KeyValue>(); 744 noJavaScriptFormParameters.addAll(generalRouteReportFormParameters); 745 Properties parameters = new Properties(); 746 for (KeyValue pair : backFormParameters) { 747 parameters.put(pair.getKey(), pair.getValue()); 748 } 749 noJavaScriptFormParameters.add(new ConcreteKeyValue(KEWConstants.RETURN_URL_ATTRIBUTE_NAME, UrlFactory.parameterizeUrl(backUrlBase, parameters))); 750 request.setAttribute("noJavaScriptFormVariables", noJavaScriptFormParameters); 751 752 return mapping.findForward(KNSConstants.MAPPING_ROUTE_REPORT); 753 } 754 755 /** 756 * route the document using the document service 757 * 758 * @param mapping 759 * @param form 760 * @param request 761 * @param response 762 * @return ActionForward 763 * @throws Exception 764 */ 765 public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 766 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 767 doProcessingAfterPost(kualiDocumentFormBase, request); 768 769 kualiDocumentFormBase.setDerivedValuesOnForm(request); 770 ActionForward preRulesForward = promptBeforeValidation(mapping, form, request, response); 771 if (preRulesForward != null) { 772 return preRulesForward; 773 } 774 775 Document document = kualiDocumentFormBase.getDocument(); 776 777 ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KNSPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "route", ""); 778 if (forward != null) { 779 return forward; 780 } 781 782 getDocumentService().routeDocument(document, kualiDocumentFormBase.getAnnotation(), combineAdHocRecipients(kualiDocumentFormBase)); 783 GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_SUCCESSFUL); 784 kualiDocumentFormBase.setAnnotation(""); 785 786 // GlobalVariables.getUserSession().addObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_OBJECT_KEY,Boolean.TRUE); 787 return mapping.findForward(RiceConstants.MAPPING_BASIC); 788 } 789 790 /** 791 * Calls the document service to blanket approve the document 792 * 793 * @param mapping 794 * @param form 795 * @param request 796 * @param response 797 * @return ActionForward 798 * @throws Exception 799 */ 800 public ActionForward blanketApprove(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 801 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 802 doProcessingAfterPost(kualiDocumentFormBase, request); 803 804 kualiDocumentFormBase.setDerivedValuesOnForm(request); 805 ActionForward preRulesForward = promptBeforeValidation(mapping, form, request, response); 806 if (preRulesForward != null) { 807 return preRulesForward; 808 } 809 810 Document document = kualiDocumentFormBase.getDocument(); 811 812 ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KNSPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "blanketApprove", ""); 813 if (forward != null) { 814 return forward; 815 } 816 817 getDocumentService().blanketApproveDocument(document, kualiDocumentFormBase.getAnnotation(), combineAdHocRecipients(kualiDocumentFormBase)); 818 GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_APPROVED); 819 kualiDocumentFormBase.setAnnotation(""); 820 return returnToSender(request, mapping, kualiDocumentFormBase); 821 } 822 823 /** 824 * Calls the document service to approve the document 825 * 826 * @param mapping 827 * @param form 828 * @param request 829 * @param response 830 * @return ActionForward 831 * @throws Exception 832 */ 833 public ActionForward approve(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 834 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 835 doProcessingAfterPost(kualiDocumentFormBase, request); 836 837 kualiDocumentFormBase.setDerivedValuesOnForm(request); 838 ActionForward preRulesForward = promptBeforeValidation(mapping, form, request, response); 839 if (preRulesForward != null) { 840 return preRulesForward; 841 } 842 843 Document document = kualiDocumentFormBase.getDocument(); 844 845 ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KNSPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "approve", ""); 846 if (forward != null) { 847 return forward; 848 } 849 850 getDocumentService().approveDocument(document, kualiDocumentFormBase.getAnnotation(), combineAdHocRecipients(kualiDocumentFormBase)); 851 GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_APPROVED); 852 kualiDocumentFormBase.setAnnotation(""); 853 return returnToSender(request, mapping, kualiDocumentFormBase); 854 } 855 856 /** 857 * Calls the document service to disapprove the document 858 * 859 * @param mapping 860 * @param form 861 * @param request 862 * @param response 863 * @return ActionForward 864 * @throws Exception 865 */ 866 public ActionForward disapprove(ActionMapping mapping, ActionForm form, HttpServletRequest request, 867 HttpServletResponse response) throws Exception { 868 Object question = request.getParameter(KNSConstants.QUESTION_INST_ATTRIBUTE_NAME); 869 String reason = request.getParameter(KNSConstants.QUESTION_REASON_ATTRIBUTE_NAME); 870 871 if (StringUtils.isBlank(reason)) { 872 String context = request.getParameter(KNSConstants.QUESTION_CONTEXT); 873 if (context != null && StringUtils.contains(context, KNSConstants.QUESTION_REASON_ATTRIBUTE_NAME + "=")) { 874 reason = StringUtils.substringAfter(context, KNSConstants.QUESTION_REASON_ATTRIBUTE_NAME + "="); 875 } 876 } 877 878 String disapprovalNoteText = ""; 879 880 // start in logic for confirming the disapproval 881 if (question == null) { 882 // ask question if not already asked 883 return this.performQuestionWithInput(mapping, form, request, response, 884 KNSConstants.DOCUMENT_DISAPPROVE_QUESTION, 885 getKualiConfigurationService().getPropertyString(RiceKeyConstants.QUESTION_DISAPPROVE_DOCUMENT), 886 KNSConstants.CONFIRMATION_QUESTION, KNSConstants.MAPPING_DISAPPROVE, ""); 887 } 888 Object buttonClicked = request.getParameter(KNSConstants.QUESTION_CLICKED_BUTTON); 889 if ((KNSConstants.DOCUMENT_DISAPPROVE_QUESTION.equals(question)) 890 && ConfirmationQuestion.NO.equals(buttonClicked)) { 891 // if no button clicked just reload the doc 892 return mapping.findForward(RiceConstants.MAPPING_BASIC); 893 } 894 895 // have to check length on value entered 896 String introNoteMessage = getKualiConfigurationService().getPropertyString( 897 RiceKeyConstants.MESSAGE_DISAPPROVAL_NOTE_TEXT_INTRO) 898 + KNSConstants.BLANK_SPACE; 899 900 // build out full message 901 disapprovalNoteText = introNoteMessage + reason; 902 903 // check for sensitive data in note 904 boolean warnForSensitiveData = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean( 905 KNSConstants.KNS_NAMESPACE, ParameterConstants.ALL_COMPONENT, 906 KNSConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS_WARNING_IND); 907 if (warnForSensitiveData) { 908 String context = KNSConstants.QUESTION_REASON_ATTRIBUTE_NAME + "=" + reason; 909 ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, 910 KNSConstants.QUESTION_REASON_ATTRIBUTE_NAME, disapprovalNoteText, "disapprove", context); 911 if (forward != null) { 912 return forward; 913 } 914 } else { 915 if (WebUtils.containsSensitiveDataPatternMatch(disapprovalNoteText)) { 916 return this 917 .performQuestionWithInputAgainBecauseOfErrors(mapping, form, request, response, 918 KNSConstants.DOCUMENT_DISAPPROVE_QUESTION, getKualiConfigurationService() 919 .getPropertyString(RiceKeyConstants.QUESTION_DISAPPROVE_DOCUMENT), 920 KNSConstants.CONFIRMATION_QUESTION, KNSConstants.MAPPING_DISAPPROVE, "", reason, 921 RiceKeyConstants.ERROR_DOCUMENT_FIELD_CONTAINS_POSSIBLE_SENSITIVE_DATA, 922 KNSConstants.QUESTION_REASON_ATTRIBUTE_NAME, "reason"); 923 } 924 } 925 926 int disapprovalNoteTextLength = disapprovalNoteText.length(); 927 928 // get note text max length from DD 929 int noteTextMaxLength = getDataDictionaryService().getAttributeMaxLength(Note.class, 930 KNSConstants.NOTE_TEXT_PROPERTY_NAME); 931 932 if (StringUtils.isBlank(reason) || (disapprovalNoteTextLength > noteTextMaxLength)) { 933 934 if (reason == null) { 935 // prevent a NPE by setting the reason to a blank string 936 reason = ""; 937 } 938 return this.performQuestionWithInputAgainBecauseOfErrors(mapping, form, request, response, 939 KNSConstants.DOCUMENT_DISAPPROVE_QUESTION, 940 getKualiConfigurationService().getPropertyString(RiceKeyConstants.QUESTION_DISAPPROVE_DOCUMENT), 941 KNSConstants.CONFIRMATION_QUESTION, KNSConstants.MAPPING_DISAPPROVE, "", reason, 942 RiceKeyConstants.ERROR_DOCUMENT_DISAPPROVE_REASON_REQUIRED, 943 KNSConstants.QUESTION_REASON_ATTRIBUTE_NAME, Integer.toString(noteTextMaxLength)); 944 } 945 946 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 947 doProcessingAfterPost(kualiDocumentFormBase, request); 948 getDocumentService().disapproveDocument(kualiDocumentFormBase.getDocument(), disapprovalNoteText); 949 GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_DISAPPROVED); 950 kualiDocumentFormBase.setAnnotation(""); 951 952 return returnToSender(request, mapping, kualiDocumentFormBase); 953 } 954 955 /** 956 * Calls the document service to cancel the document 957 * 958 * @param mapping 959 * @param form 960 * @param request 961 * @param response 962 * @return ActionForward 963 * @throws Exception 964 */ 965 public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 966 Object question = request.getParameter(KNSConstants.QUESTION_INST_ATTRIBUTE_NAME); 967 // this should probably be moved into a private instance variable 968 // logic for cancel question 969 if (question == null) { 970 // ask question if not already asked 971 return this.performQuestionWithoutInput(mapping, form, request, response, KNSConstants.DOCUMENT_CANCEL_QUESTION, getKualiConfigurationService().getPropertyString("document.question.cancel.text"), KNSConstants.CONFIRMATION_QUESTION, KNSConstants.MAPPING_CANCEL, ""); 972 } else { 973 Object buttonClicked = request.getParameter(KNSConstants.QUESTION_CLICKED_BUTTON); 974 if ((KNSConstants.DOCUMENT_CANCEL_QUESTION.equals(question)) && ConfirmationQuestion.NO.equals(buttonClicked)) { 975 // if no button clicked just reload the doc 976 return mapping.findForward(RiceConstants.MAPPING_BASIC); 977 } 978 // else go to cancel logic below 979 } 980 981 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 982 doProcessingAfterPost(kualiDocumentFormBase, request); 983 // KULRICE-4447 Call cancelDocument() only if the document exists 984 if (getDocumentService().documentExists(kualiDocumentFormBase.getDocId())) { 985 getDocumentService().cancelDocument(kualiDocumentFormBase.getDocument(), kualiDocumentFormBase.getAnnotation()); 986 } 987 988 return returnToSender(request, mapping, kualiDocumentFormBase); 989 } 990 991 /** 992 * Close the document and take the user back to the index; only after asking the user if they want to save the document first. 993 * Only users who have the "canSave()" permission are given this option. 994 * 995 * @param mapping 996 * @param form 997 * @param request 998 * @param response 999 * @return ActionForward 1000 * @throws Exception 1001 */ 1002 public ActionForward close(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1003 KualiDocumentFormBase docForm = (KualiDocumentFormBase) form; 1004 doProcessingAfterPost(docForm, request); 1005 Document document = docForm.getDocument(); 1006 // only want to prompt them to save if they already can save 1007 if (canSave(docForm)) { 1008 Object question = getQuestion(request); 1009 // logic for close question 1010 if (question == null) { 1011 // ask question if not already asked 1012 return this.performQuestionWithoutInput(mapping, form, request, response, KNSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION, getKualiConfigurationService().getPropertyString(RiceKeyConstants.QUESTION_SAVE_BEFORE_CLOSE), KNSConstants.CONFIRMATION_QUESTION, KNSConstants.MAPPING_CLOSE, ""); 1013 } else { 1014 Object buttonClicked = request.getParameter(KNSConstants.QUESTION_CLICKED_BUTTON); 1015 if ((KNSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION.equals(question)) && ConfirmationQuestion.YES.equals(buttonClicked)) { 1016 // if yes button clicked - save the doc 1017 ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KNSPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "save", ""); 1018 if (forward != null) { 1019 return forward; 1020 } 1021 1022 getDocumentService().saveDocument(docForm.getDocument()); 1023 } 1024 // else go to close logic below 1025 } 1026 } 1027 1028 return returnToSender(request, mapping, docForm); 1029 } 1030 1031 protected boolean canSave(ActionForm form) { 1032 KualiDocumentFormBase docForm = (KualiDocumentFormBase) form; 1033 return docForm.getDocumentActions().containsKey(KNSConstants.KUALI_ACTION_CAN_SAVE); 1034 } 1035 1036 protected Object getQuestion(HttpServletRequest request) { 1037 return request.getParameter(KNSConstants.QUESTION_INST_ATTRIBUTE_NAME); 1038 } 1039 1040 /** 1041 * call the document service to clear the fyis 1042 * 1043 * @param mapping 1044 * @param form 1045 * @param request 1046 * @param response 1047 * @return ActionForward 1048 * @throws Exception 1049 */ 1050 public ActionForward fyi(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1051 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 1052 doProcessingAfterPost(kualiDocumentFormBase, request); 1053 getDocumentService().clearDocumentFyi(kualiDocumentFormBase.getDocument(), combineAdHocRecipients(kualiDocumentFormBase)); 1054 GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_FYIED); 1055 kualiDocumentFormBase.setAnnotation(""); 1056 return returnToSender(request, mapping, kualiDocumentFormBase); 1057 } 1058 1059 /** 1060 * call the document service to acknowledge 1061 * 1062 * @param mapping 1063 * @param form 1064 * @param request 1065 * @param response 1066 * @return ActionForward 1067 * @throws Exception 1068 */ 1069 public ActionForward acknowledge(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1070 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 1071 doProcessingAfterPost(kualiDocumentFormBase, request); 1072 getDocumentService().acknowledgeDocument(kualiDocumentFormBase.getDocument(), kualiDocumentFormBase.getAnnotation(), combineAdHocRecipients(kualiDocumentFormBase)); 1073 GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_ACKNOWLEDGED); 1074 kualiDocumentFormBase.setAnnotation(""); 1075 return returnToSender(request, mapping, kualiDocumentFormBase); 1076 } 1077 1078 /** 1079 * redirect to the supervisor functions that exist. 1080 * 1081 * @param mapping 1082 * @param form 1083 * @param request 1084 * @param response 1085 * @return ActionForward 1086 * @throws Exception 1087 */ 1088 public ActionForward supervisorFunctions(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1089 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 1090 1091 1092 String workflowSuperUserUrl = getKualiConfigurationService().getPropertyString(KNSConstants.WORKFLOW_URL_KEY) + "/SuperUser.do?methodToCall=displaySuperUserDocument&documentId=" + kualiDocumentFormBase.getDocument().getDocumentHeader().getDocumentNumber(); 1093 response.sendRedirect(workflowSuperUserUrl); 1094 1095 return null; 1096 } 1097 1098 /** 1099 * Convenience method to combine the two lists of ad hoc recipients into one which should be done before calling any of the 1100 * document service methods that expect a list of ad hoc recipients 1101 * 1102 * @param kualiDocumentFormBase 1103 * @return List 1104 */ 1105 protected List<AdHocRouteRecipient> combineAdHocRecipients(KualiDocumentFormBase kualiDocumentFormBase) { 1106 List<AdHocRouteRecipient> adHocRecipients = new ArrayList<AdHocRouteRecipient>(); 1107 adHocRecipients.addAll(kualiDocumentFormBase.getAdHocRoutePersons()); 1108 adHocRecipients.addAll(kualiDocumentFormBase.getAdHocRouteWorkgroups()); 1109 return adHocRecipients; 1110 } 1111 1112 /** 1113 * if the action desires to retain error messages generated by the rules framework for save/submit/etc. validation after returning from a lookup. 1114 * 1115 * @see org.kuali.rice.kns.web.struts.action.KualiAction#refresh(org.apache.struts.action.ActionMapping, 1116 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 1117 */ 1118 @Override 1119 public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1120 KualiDocumentFormBase kualiForm = (KualiDocumentFormBase) form; 1121 kualiForm.setDerivedValuesOnForm(request); 1122 1123 super.refresh(mapping, form, request, response); 1124 refreshAdHocRoutingWorkgroupLookups(request, kualiForm); 1125 1126 return mapping.findForward(RiceConstants.MAPPING_BASIC); 1127 } 1128 1129 /** 1130 * special refresh needed to get the workgroups populated correctly when coming back from workgroup lookups 1131 * 1132 * @param request 1133 * @param kualiForm 1134 * @throws WorkflowException 1135 */ 1136 @SuppressWarnings("unchecked") 1137 protected void refreshAdHocRoutingWorkgroupLookups(HttpServletRequest request, KualiDocumentFormBase kualiForm) throws WorkflowException { 1138 for (Enumeration<String> i = request.getParameterNames(); i.hasMoreElements();) { 1139 String parameterName = i.nextElement(); 1140 if (parameterName.equals("newAdHocRouteWorkgroup.recipientName") && !"".equals(request.getParameter(parameterName))) { 1141 //check for namespace 1142 String namespace = KimConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE; 1143 if (request.getParameter("newAdHocRouteWorkgroup.recipientNamespaceCode") != null && !"".equals(request.getParameter("newAdHocRouteWorkgroup.recipientName").trim())) { 1144 namespace = request.getParameter("newAdHocRouteWorkgroup.recipientNamespaceCode").trim(); 1145 } 1146 Group group = getIdentityManagementService().getGroupByName(namespace, request.getParameter(parameterName)); 1147 if (group != null) { 1148 kualiForm.getNewAdHocRouteWorkgroup().setId(group.getId()); 1149 kualiForm.getNewAdHocRouteWorkgroup().setRecipientName(group.getName()); 1150 kualiForm.getNewAdHocRouteWorkgroup().setRecipientNamespaceCode(group.getNamespaceCode()); 1151 } else { 1152 throw new RuntimeException("Invalid workgroup id passed as parameter."); 1153 } 1154 } 1155 if (parameterName.startsWith("adHocRouteWorkgroup[") && !"".equals(request.getParameter(parameterName))) { 1156 if (parameterName.endsWith(".recipientName")) { 1157 int lineNumber = Integer.parseInt(StringUtils.substringBetween(parameterName, "[", "]")); 1158 //check for namespace 1159 String namespaceParam = "adHocRouteWorkgroup[" + lineNumber + "].recipientNamespaceCode"; 1160 String namespace = KimConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE; 1161 if (request.getParameter(namespaceParam) != null && !"".equals(request.getParameter(namespaceParam).trim())) { 1162 namespace = request.getParameter(namespaceParam).trim(); 1163 } 1164 Group group = getIdentityManagementService().getGroupByName(namespace, request.getParameter(parameterName)); 1165 if (group != null) { 1166 kualiForm.getAdHocRouteWorkgroup(lineNumber).setId(group.getId()); 1167 kualiForm.getAdHocRouteWorkgroup(lineNumber).setRecipientName(group.getName()); 1168 kualiForm.getAdHocRouteWorkgroup(lineNumber).setRecipientNamespaceCode(group.getNamespaceCode()); 1169 } else { 1170 throw new RuntimeException("Invalid workgroup id passed as parameter."); 1171 } 1172 } 1173 } 1174 /* 1175 if (parameterName.startsWith("newAdHocRouteWorkgroup[") && !"".equals(request.getParameter(parameterName))) { 1176 if (parameterName.endsWith(".recipientName")) { 1177 int lineNumber = Integer.parseInt(StringUtils.substringBetween(parameterName, "[", "]")); 1178 //check for namespace 1179 String namespaceParam = "newAdHocRouteWorkgroup[" + lineNumber + "].recipientNamespaceCode"; 1180 String namespace = KimConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE; 1181 if (request.getParameter(namespaceParam) != null && !"".equals(request.getParameter(namespaceParam).trim())) { 1182 namespace = request.getParameter(namespaceParam).trim(); 1183 } 1184 KimGroup group = getIdentityManagementService().getGroupByName(namespace, request.getParameter(parameterName)); 1185 if (group != null) { 1186 kualiForm.getAdHocRouteWorkgroup(lineNumber).setId(group.getGroupId()); 1187 kualiForm.getAdHocRouteWorkgroup(lineNumber).setRecipientName(group.getGroupName()); 1188 kualiForm.getAdHocRouteWorkgroup(lineNumber).setRecipientNamespaceCode(group.getNamespaceCode()); 1189 } else { 1190 throw new RuntimeException("Invalid workgroup id passed as parameter."); 1191 } 1192 } 1193 } 1194 */ 1195 } 1196 } 1197 1198 1199 /** 1200 * Cancels the pending attachment, if any. 1201 * 1202 * @param mapping 1203 * @param form 1204 * @param request 1205 * @param response 1206 * @return ActionForward 1207 * @throws Exception 1208 */ 1209 public ActionForward cancelBOAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1210 KualiDocumentFormBase documentForm = (KualiDocumentFormBase) form; 1211 1212 // blank current attachmentFile 1213 documentForm.setAttachmentFile(new BlankFormFile()); 1214 1215 // remove current attachment, if any 1216 Note note = documentForm.getNewNote(); 1217 note.removeAttachment(); 1218 documentForm.setNewNote(note); 1219 1220 return mapping.findForward(RiceConstants.MAPPING_BASIC); 1221 } 1222 1223 /** 1224 * Handy method to stream the byte array to response object 1225 * 1226 * @param fileContents 1227 * @param fileName 1228 * @param fileContentType 1229 * @param response 1230 * @throws Exception 1231 */ 1232 protected void streamToResponse(byte[] fileContents, String fileName, String fileContentType, HttpServletResponse response) throws Exception { 1233 ByteArrayOutputStream baos = null; 1234 try { 1235 baos = new ByteArrayOutputStream(fileContents.length); 1236 baos.write(fileContents); 1237 WebUtils.saveMimeOutputStreamAsFile(response, fileContentType, baos, fileName); 1238 } finally { 1239 try { 1240 if (baos != null) { 1241 baos.close(); 1242 baos = null; 1243 } 1244 } catch (IOException ioEx) { 1245 LOG.error("Error while downloading attachment"); 1246 throw new RuntimeException("IOException occurred while downloading attachment", ioEx); 1247 } 1248 } 1249 } 1250 1251 /** 1252 * Downloads the selected attachment to the user's browser 1253 * 1254 * @param mapping 1255 * @param form 1256 * @param request 1257 * @param response 1258 * @return ActionForward 1259 * @throws Exception 1260 */ 1261 public ActionForward downloadBOAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1262 KualiDocumentFormBase documentForm = (KualiDocumentFormBase) form; 1263 1264 int attachmentIndex = selectedAttachmentIndex(request); 1265 if (attachmentIndex >= 0) { 1266 Note note = documentForm.getDocument().getNote(attachmentIndex); 1267 Attachment attachment = note.getAttachment(); 1268 //make sure attachment is setup with backwards reference to note (rather then doing this we could also just call the attachment service (with a new method that took in the note) 1269 attachment.setNote(note); 1270 1271 // since we're downloading a file, all of the editable properties from the previous request will continue to be editable. 1272 documentForm.copyPopulateEditablePropertiesToActionEditableProperties(); 1273 1274 WebUtils.saveMimeInputStreamAsFile(response, attachment.getAttachmentMimeTypeCode(), attachment.getAttachmentContents(), attachment.getAttachmentFileName(), attachment.getAttachmentFileSize().intValue()); 1275 return null; 1276 } 1277 1278 return mapping.findForward(RiceConstants.MAPPING_BASIC); 1279 } 1280 1281 1282 /** 1283 * @param request 1284 * @return index of the attachment whose download button was just pressed 1285 */ 1286 protected int selectedAttachmentIndex(HttpServletRequest request) { 1287 int attachmentIndex = -1; 1288 1289 String parameterName = (String) request.getAttribute(KNSConstants.METHOD_TO_CALL_ATTRIBUTE); 1290 if (StringUtils.isNotBlank(parameterName)) { 1291 String attachmentIndexParam = StringUtils.substringBetween(parameterName, ".attachment[", "]."); 1292 1293 try { 1294 attachmentIndex = Integer.parseInt(attachmentIndexParam); 1295 } catch (NumberFormatException ignored) { 1296 } 1297 } 1298 1299 return attachmentIndex; 1300 } 1301 1302 1303 /** 1304 * insert a note into the document 1305 * 1306 * @param mapping 1307 * @param form 1308 * @param request 1309 * @param response 1310 * @return ActionForward 1311 * @throws Exception 1312 */ 1313 public ActionForward insertBONote(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1314 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 1315 Document document = kualiDocumentFormBase.getDocument(); 1316 Note newNote = kualiDocumentFormBase.getNewNote(); 1317 newNote.setNotePostedTimestampToCurrent(); 1318 1319 String attachmentTypeCode = null; 1320 1321 FormFile attachmentFile = kualiDocumentFormBase.getAttachmentFile(); 1322 if (attachmentFile == null) { 1323 GlobalVariables.getMessageMap().putError( 1324 String.format("%s.%s", 1325 KNSConstants.NEW_DOCUMENT_NOTE_PROPERTY_NAME, 1326 KNSConstants.NOTE_ATTACHMENT_FILE_PROPERTY_NAME), 1327 RiceKeyConstants.ERROR_UPLOADFILE_NULL); 1328 // This line was removed in order to continue to validates other 1329 // return mapping.findForward(RiceConstants.MAPPING_BASIC); 1330 } 1331 1332 if (newNote.getAttachment() != null) { 1333 attachmentTypeCode = newNote.getAttachment().getAttachmentTypeCode(); 1334 } 1335 1336 // check authorization for adding notes 1337 DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(document); 1338 if (!documentAuthorizer.canAddNoteAttachment(document, attachmentTypeCode, GlobalVariables.getUserSession().getPerson())) { 1339 throw buildAuthorizationException("annotate", document); 1340 } 1341 1342 // create the attachment first, so that failure-to-create-attachment can be treated as a validation failure 1343 1344 Attachment attachment = null; 1345 if (attachmentFile != null && !StringUtils.isBlank(attachmentFile.getFileName())) { 1346 if (attachmentFile.getFileSize() == 0) { 1347 GlobalVariables.getMessageMap().putError( 1348 String.format("%s.%s", 1349 KNSConstants.NEW_DOCUMENT_NOTE_PROPERTY_NAME, 1350 KNSConstants.NOTE_ATTACHMENT_FILE_PROPERTY_NAME), 1351 RiceKeyConstants.ERROR_UPLOADFILE_EMPTY, 1352 attachmentFile.getFileName()); 1353 // This line was removed in order to continue to validates other 1354 // return mapping.findForward(RiceConstants.MAPPING_BASIC); 1355 } else { 1356 String attachmentType = null; 1357 Attachment newAttachment = kualiDocumentFormBase.getNewNote().getAttachment(); 1358 if (newAttachment != null) { 1359 attachmentType = newAttachment.getAttachmentTypeCode(); 1360 } 1361 attachment = getAttachmentService().createAttachment(document.getNoteTarget(), attachmentFile.getFileName(), attachmentFile.getContentType(), attachmentFile.getFileSize(), attachmentFile.getInputStream(), attachmentType); 1362 } 1363 } 1364 1365 DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary(); 1366 DocumentEntry entry = dataDictionary.getDocumentEntry(document.getClass().getName()); 1367 1368 if (entry.getDisplayTopicFieldInNotes()) { 1369 String topicText = kualiDocumentFormBase.getNewNote().getNoteTopicText(); 1370 if (StringUtils.isBlank(topicText)) { 1371 GlobalVariables.getMessageMap().putError( 1372 String.format("%s.%s", 1373 KNSConstants.NEW_DOCUMENT_NOTE_PROPERTY_NAME, 1374 KNSConstants.NOTE_TOPIC_TEXT_PROPERTY_NAME), 1375 RiceKeyConstants.ERROR_REQUIRED, 1376 "Note Topic (Note Topic)"); 1377 } 1378 } 1379 1380 // create a new note from the data passed in 1381 // TODO gah! this is awful 1382 Person kualiUser = GlobalVariables.getUserSession().getPerson(); 1383 if (kualiUser == null) { 1384 throw new IllegalStateException("Current UserSession has a null Person."); 1385 } 1386 Note tmpNote = getNoteService().createNote(newNote, document.getNoteTarget(), kualiUser.getPrincipalId()); 1387 1388 ActionForward forward = checkAndWarnAboutSensitiveData(mapping, form, request, response, KNSPropertyConstants.NOTE, tmpNote.getNoteText(), "insertBONote", ""); 1389 if (forward != null) { 1390 return forward; 1391 } 1392 1393 // validate the note 1394 boolean rulePassed = getKualiRuleService().applyRules(new AddNoteEvent(document, tmpNote)); 1395 1396 // if the rule evaluation passed, let's add the note 1397 if (rulePassed) { 1398 tmpNote.refresh(); 1399 1400 1401 DocumentHeader documentHeader = document.getDocumentHeader(); 1402 1403 // associate note with object now 1404 document.addNote(tmpNote); 1405 1406 // persist the note if the document is already saved the getObjectId check is to get around a bug with certain documents where 1407 // "saved" doesn't really persist, if you notice any problems with missing notes check this line 1408 //maintenance document BO note should only be saved into table when document is in the PROCESSED workflow status 1409 if (!documentHeader.getWorkflowDocument().stateIsInitiated() && StringUtils.isNotEmpty(document.getNoteTarget().getObjectId()) 1410 && !(document instanceof MaintenanceDocument && NoteType.BUSINESS_OBJECT.getCode().equals(tmpNote.getNoteTypeCode())) 1411 ) { 1412 getNoteService().save(tmpNote); 1413 } 1414 // adding the attachment after refresh gets called, since the attachment record doesn't get persisted 1415 // until the note does (and therefore refresh doesn't have any attachment to autoload based on the id, nor does it 1416 // autopopulate the id since the note hasn't been persisted yet) 1417 if (attachment != null) { 1418 tmpNote.addAttachment(attachment); 1419 // save again for attachment, note this is because sometimes the attachment is added first to the above then ojb tries to save 1420 //without the PK on the attachment I think it is safer then trying to get the sequence manually 1421 if (!documentHeader.getWorkflowDocument().stateIsInitiated() && StringUtils.isNotEmpty(document.getNoteTarget().getObjectId()) 1422 && !(document instanceof MaintenanceDocument && NoteType.BUSINESS_OBJECT.getCode().equals(tmpNote.getNoteTypeCode())) 1423 ) { 1424 getNoteService().save(tmpNote); 1425 } 1426 } 1427 1428 1429 // reset the new note back to an empty one 1430 kualiDocumentFormBase.setNewNote(new Note()); 1431 } 1432 1433 1434 return mapping.findForward(RiceConstants.MAPPING_BASIC); 1435 } 1436 1437 /** 1438 * delete a note from the document 1439 * 1440 * @param mapping 1441 * @param form 1442 * @param request 1443 * @param response 1444 * @return ActionForward 1445 * @throws Exception 1446 */ 1447 public ActionForward deleteBONote(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1448 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 1449 Document document = kualiDocumentFormBase.getDocument(); 1450 1451 1452 // DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary(); 1453 // DocumentEntry entry = dataDictionary.getDocumentEntry(document.getClass().getName()); 1454 1455 // check authorization for adding notes 1456 //DocumentActionFlags flags = getDocumentActionFlags(document); 1457 //if (!kualiDocumentFormBase.getDocumentActions().containsKey(KNSConstants.KUALI_ACTION_CAN_ANNOTATE)) { 1458 // buildAuthorizationException("annotate", document); 1459 // return mapping.findForward(RiceConstants.MAPPING_BASIC); 1460 //} 1461 1462 // ok to delete the note/attachment 1463 // derive the note property from the newNote on the form 1464 Note newNote = kualiDocumentFormBase.getNewNote(); 1465 Note note = document.getNote(getLineToDelete(request)); 1466 Attachment attachment = note.getAttachment(); 1467 String attachmentTypeCode = null; 1468 if (attachment != null) { 1469 attachmentTypeCode = attachment.getAttachmentTypeCode(); 1470 } 1471 String authorUniversalIdentifier = note.getAuthorUniversalIdentifier(); 1472 if (!WebUtils.canDeleteNoteAttachment(document, attachmentTypeCode, authorUniversalIdentifier)) { 1473 throw buildAuthorizationException("annotate", document); 1474 } 1475 1476 if (attachment != null) { // only do this if the note has been persisted 1477 //KFSMI-798 - refresh() changed to refreshNonUpdateableReferences() 1478 //All references for the business object Attachment are auto-update="none", 1479 //so refreshNonUpdateableReferences() should work the same as refresh() 1480 if (note.getNoteIdentifier() != null) { // KULRICE-2343 don't blow away note reference if the note wasn't persisted 1481 attachment.refreshNonUpdateableReferences(); 1482 } 1483 getAttachmentService().deleteAttachmentContents(attachment); 1484 } 1485 // delete the note if the document is already saved 1486 if (!document.getDocumentHeader().getWorkflowDocument().stateIsInitiated()) { 1487 getNoteService().deleteNote(note); 1488 } 1489 document.removeNote(note); 1490 1491 return mapping.findForward(RiceConstants.MAPPING_BASIC); 1492 } 1493 1494 /** 1495 * Override this to customize which routing action to take when sending a note. This method reads the system parameter 1496 * KR-NS/Document/SEND_NOTE_WORKFLOW_NOTIFICATION_ACTIONS to determine which action to take 1497 * 1498 * @param request 1499 * @param note 1500 * @return a value from {@link KEWConstants} 1501 */ 1502 protected String determineNoteWorkflowNotificationAction(HttpServletRequest request, KualiDocumentFormBase kualiDocumentFormBase, Note note) { 1503 return getParameterService().getParameterValueAsString(KNSConstants.KNS_NAMESPACE, KNSConstants.DetailTypes.DOCUMENT_DETAIL_TYPE, KNSConstants.SEND_NOTE_WORKFLOW_NOTIFICATION_ACTIONS_PARM_NM); 1504 } 1505 1506 public ActionForward sendNoteWorkflowNotification(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1507 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 1508 Document document = kualiDocumentFormBase.getDocument(); 1509 1510 Note note = document.getNote(getSelectedLine(request)); 1511 1512 // verify recipient was specified 1513 if (StringUtils.isBlank(note.getAdHocRouteRecipient().getId())) { 1514 GlobalVariables.getMessageMap().putError(KNSPropertyConstants.NEW_DOCUMENT_NOTE, RiceKeyConstants.ERROR_SEND_NOTE_NOTIFICATION_RECIPIENT); 1515 return mapping.findForward(RiceConstants.MAPPING_BASIC); 1516 } 1517 // check recipient is valid 1518 else { 1519 note.getAdHocRouteRecipient().setActionRequested(determineNoteWorkflowNotificationAction(request, kualiDocumentFormBase, note)); 1520 1521 boolean rulePassed = getKualiRuleService().applyRules(new AddAdHocRoutePersonEvent(KNSPropertyConstants.NEW_DOCUMENT_NOTE, document, (AdHocRoutePerson) note.getAdHocRouteRecipient())); 1522 if (!rulePassed) { 1523 return mapping.findForward(RiceConstants.MAPPING_BASIC); 1524 } 1525 } 1526 1527 // if document is saved, send notification 1528 if (!document.getDocumentHeader().getWorkflowDocument().stateIsInitiated()) { 1529 getDocumentService().sendNoteRouteNotification(document, note, GlobalVariables.getUserSession().getPerson()); 1530 1531 // add success message 1532 GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_SEND_NOTE_NOTIFICATION_SUCCESSFUL); 1533 } else { 1534 GlobalVariables.getMessageMap().putError(KNSPropertyConstants.NEW_DOCUMENT_NOTE, RiceKeyConstants.ERROR_SEND_NOTE_NOTIFICATION_DOCSTATUS); 1535 } 1536 1537 return mapping.findForward(RiceConstants.MAPPING_BASIC); 1538 } 1539 1540 1541 /** 1542 * Generates detailed log messages for OptimisticLockExceptions 1543 * 1544 * @param e 1545 */ 1546 private final void logOjbOptimisticLockException(OptimisticLockException e) { 1547 if (LOG.isInfoEnabled()) { 1548 StringBuffer message = new StringBuffer("caught OptimisticLockException, caused by "); 1549 Object sourceObject = e.getSourceObject(); 1550 String infix = null; 1551 try { 1552 // try to add instance details 1553 infix = sourceObject.toString(); 1554 } catch (Exception e2) { 1555 // just use the class name 1556 infix = sourceObject.getClass().getName(); 1557 } 1558 message.append(infix); 1559 1560 if (sourceObject instanceof PersistableBusinessObject) { 1561 PersistableBusinessObject persistableObject = (PersistableBusinessObject) sourceObject; 1562 message.append(" [versionNumber = ").append(persistableObject.getVersionNumber()).append("]"); 1563 } 1564 1565 LOG.info(message.toString(), e); 1566 } 1567 } 1568 1569 1570 /** 1571 * Makes calls to the PromptBeforeValidation specified for the document. If the class returns an actionforward, that forward 1572 * will be returned (thus controlling how execution occurs), or null. 1573 * 1574 * @param mapping 1575 * @param form 1576 * @param request 1577 * @param response 1578 * @return 1579 * @throws Exception 1580 */ 1581 public ActionForward promptBeforeValidation(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1582 return promptBeforeValidation(mapping, form, request, response, "route"); 1583 } 1584 1585 /** 1586 * Makes calls to the PromptBeforeValidation specified for the document. If the class returns an actionforward, that forward 1587 * will be returned (thus controlling how execution occurs), or null. 1588 * 1589 * @param mapping 1590 * @param form 1591 * @param request 1592 * @param response 1593 * @param methodToCall 1594 * @return 1595 * @throws Exception 1596 */ 1597 public ActionForward promptBeforeValidation(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String methodToCall) throws Exception { 1598 KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; 1599 1600 /* callback to any pre rules check class */ 1601 Class<? extends PromptBeforeValidation> promptBeforeValidationClass = getDataDictionaryService().getPromptBeforeValidationClass(kualiDocumentFormBase.getDocTypeName()); 1602 if (LOG.isDebugEnabled()) { 1603 LOG.debug("PromptBeforeValidationClass: " + promptBeforeValidationClass); 1604 } 1605 if (promptBeforeValidationClass != null) { 1606 PromptBeforeValidation promptBeforeValidation = promptBeforeValidationClass.newInstance(); 1607 PromptBeforeValidationEvent event = new PromptBeforeValidationEvent("Pre Maint route Check", "", kualiDocumentFormBase.getDocument()); 1608 boolean continueRoute = promptBeforeValidation.processPrompts(form, request, event); 1609 if (!continueRoute) { 1610 if (event.isPerformQuestion()) { 1611 return super.performQuestionWithoutInput(mapping, kualiDocumentFormBase, request, response, event.getQuestionId(), event.getQuestionText(), event.getQuestionType(), methodToCall, event.getQuestionContext()); 1612 } else { 1613 // This error section is here to avoid a silent and very confusing failure. If the PreRule 1614 // instance returns a null for the processPreRuleChecks above, but does not set an 1615 // ActionForwardName on the event, processing will just silently fail here, and the user 1616 // will be presented with a blank frame. 1617 // 1618 // If the processPreRuleCheck() returns a false, an ActionForwardName needs to be set before hand 1619 // by the PreRule class. 1620 ActionForward actionForward = mapping.findForward(event.getActionForwardName()); 1621 if (actionForward == null) { 1622 throw new RuntimeException("No ActionForwardName defined on this Event, no further actions will be processed."); 1623 } 1624 return actionForward; 1625 } 1626 } 1627 } 1628 1629 return null; 1630 } 1631 1632 1633 /** 1634 * Convenience method for building authorization exceptions 1635 * 1636 * @param action 1637 * @param document 1638 */ 1639 protected DocumentAuthorizationException buildAuthorizationException(String action, Document document) { 1640 return new DocumentAuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(), action, document.getDocumentNumber()); 1641 } 1642 1643 protected boolean exitingDocument() { 1644 String methodCalledViaDispatch = (String) GlobalVariables.getUserSession().retrieveObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_OBJECT_KEY); 1645 String methodCompleted = (String) GlobalVariables.getUserSession().retrieveObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_OBJECT_KEY); 1646 return StringUtils.isNotEmpty(methodCompleted) && StringUtils.isNotEmpty(methodCalledViaDispatch) && methodCompleted.startsWith(methodCalledViaDispatch); 1647 } 1648 1649 protected void setupDocumentExit() { 1650 String methodCalledViaDispatch = (String) GlobalVariables.getUserSession().retrieveObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_OBJECT_KEY); 1651 if(StringUtils.isNotEmpty(methodCalledViaDispatch)) { 1652 GlobalVariables.getUserSession().addObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_OBJECT_KEY, (Object) (methodCalledViaDispatch + DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_MARKER)); 1653 } 1654 } 1655 1656 /** 1657 * If the given form has returnToActionList set to true, this method returns an ActionForward that should take the user back to 1658 * their action list; otherwise, it returns them to the portal. 1659 * 1660 * @param form 1661 * @return 1662 */ 1663 protected ActionForward returnToSender(HttpServletRequest request, ActionMapping mapping, KualiDocumentFormBase form) { 1664 final ActionForward dest; 1665 if (form.isReturnToActionList()) { 1666 String workflowBase = getKualiConfigurationService().getPropertyString(KNSConstants.WORKFLOW_URL_KEY); 1667 String actionListUrl = workflowBase + "/ActionList.do"; 1668 1669 dest = new ActionForward(actionListUrl, true); 1670 } else if (StringUtils.isNotBlank(form.getBackLocation())) { 1671 dest = new ActionForward(form.getBackLocation(), true); 1672 } else { 1673 dest = mapping.findForward(KNSConstants.MAPPING_PORTAL); 1674 } 1675 1676 setupDocumentExit(); 1677 return dest; 1678 } 1679 1680 @SuppressWarnings("unchecked") 1681 protected void populateAuthorizationFields(KualiDocumentFormBase formBase) { 1682 if (formBase.isFormDocumentInitialized()) { 1683 Document document = formBase.getDocument(); 1684 Person user = GlobalVariables.getUserSession().getPerson(); 1685 DocumentPresentationController documentPresentationController = KNSServiceLocatorWeb.getDocumentHelperService().getDocumentPresentationController(document); 1686 DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(document); 1687 Set<String> documentActions = documentPresentationController.getDocumentActions(document); 1688 documentActions = documentAuthorizer.getDocumentActions(document, user, documentActions); 1689 1690 if (getDataDictionaryService().getDataDictionary().getDocumentEntry(document.getClass().getName()).getUsePessimisticLocking()) { 1691 documentActions = getPessimisticLockService().getDocumentActions(document, user, documentActions); 1692 } 1693 1694 //DocumentActionFlags flags = new DocumentActionFlags(); 1695 formBase.setDocumentActions(convertSetToMap(documentActions)); 1696 1697 } 1698 } 1699 1700 protected void populateAdHocActionRequestCodes(KualiDocumentFormBase formBase) { 1701 Document document = formBase.getDocument(); 1702 DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(document); 1703 Map<String, String> adHocActionRequestCodes = new HashMap<String, String>(); 1704 1705 if (documentAuthorizer.canSendAdHocRequests(document, KEWConstants.ACTION_REQUEST_FYI_REQ, GlobalVariables.getUserSession().getPerson())) { 1706 adHocActionRequestCodes.put(KEWConstants.ACTION_REQUEST_FYI_REQ, KEWConstants.ACTION_REQUEST_FYI_REQ_LABEL); 1707 } 1708 if (!document.getDocumentHeader().getWorkflowDocument().stateIsFinal() && documentAuthorizer.canSendAdHocRequests(document, KEWConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, GlobalVariables.getUserSession().getPerson())) { 1709 adHocActionRequestCodes.put(KEWConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, KEWConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ_LABEL); 1710 } 1711 if (!(document.getDocumentHeader().getWorkflowDocument().stateIsApproved() || document.getDocumentHeader().getWorkflowDocument().stateIsProcessed() || document.getDocumentHeader().getWorkflowDocument().stateIsFinal()) && documentAuthorizer.canSendAdHocRequests(document, KEWConstants.ACTION_REQUEST_APPROVE_REQ, GlobalVariables.getUserSession().getPerson())) { 1712 adHocActionRequestCodes.put(KEWConstants.ACTION_REQUEST_APPROVE_REQ, KEWConstants.ACTION_REQUEST_APPROVE_REQ_LABEL); 1713 } 1714 1715 formBase.setAdHocActionRequestCodes(adHocActionRequestCodes); 1716 1717 } 1718 1719 1720 @SuppressWarnings("unchecked") 1721 protected Map convertSetToMap(Set s) { 1722 Map map = new HashMap(); 1723 Iterator i = s.iterator(); 1724 while (i.hasNext()) { 1725 Object key = i.next(); 1726 map.put(key, KNSConstants.KUALI_DEFAULT_TRUE_VALUE); 1727 } 1728 return map; 1729 } 1730 1731 /** 1732 * @return the dataDictionaryService 1733 */ 1734 protected DataDictionaryService getDataDictionaryService() { 1735 if (dataDictionaryService == null) { 1736 dataDictionaryService = KNSServiceLocatorWeb.getDataDictionaryService(); 1737 } 1738 return dataDictionaryService; 1739 } 1740 1741 protected DocumentHelperService getDocumentHelperService() { 1742 if (documentHelperService == null) { 1743 documentHelperService = KNSServiceLocatorWeb.getDocumentHelperService(); 1744 } 1745 return this.documentHelperService; 1746 } 1747 1748 protected DocumentService getDocumentService() { 1749 if (documentService == null) { 1750 documentService = KNSServiceLocatorWeb.getDocumentService(); 1751 } 1752 return this.documentService; 1753 } 1754 1755 protected ConfigurationService getKualiConfigurationService() { 1756 if (kualiConfigurationService == null) { 1757 kualiConfigurationService = KNSServiceLocator.getKualiConfigurationService(); 1758 } 1759 return this.kualiConfigurationService; 1760 } 1761 1762 protected ParameterService getParameterService() { 1763 if (parameterService == null) { 1764 parameterService = CoreFrameworkServiceLocator.getParameterService(); 1765 } 1766 return this.parameterService; 1767 } 1768 1769 protected PessimisticLockService getPessimisticLockService() { 1770 if (pessimisticLockService == null) { 1771 pessimisticLockService = KNSServiceLocatorWeb.getPessimisticLockService(); 1772 } 1773 return this.pessimisticLockService; 1774 } 1775 1776 protected KualiRuleService getKualiRuleService() { 1777 if (kualiRuleService == null) { 1778 kualiRuleService = KNSServiceLocatorWeb.getKualiRuleService(); 1779 } 1780 return this.kualiRuleService; 1781 } 1782 1783 protected IdentityManagementService getIdentityManagementService() { 1784 if (identityManagementService == null) { 1785 identityManagementService = KimApiServiceLocator.getIdentityManagementService(); 1786 } 1787 return this.identityManagementService; 1788 } 1789 1790 protected AttachmentService getAttachmentService() { 1791 if (attachmentService == null) { 1792 attachmentService = KNSServiceLocator.getAttachmentService(); 1793 } 1794 return this.attachmentService; 1795 } 1796 1797 protected NoteService getNoteService() { 1798 if (noteService == null) { 1799 noteService = KNSServiceLocator.getNoteService(); 1800 } 1801 return this.noteService; 1802 } 1803 1804 protected BusinessObjectService getBusinessObjectService() { 1805 if (businessObjectService == null) { 1806 businessObjectService = KNSServiceLocator.getBusinessObjectService(); 1807 } 1808 return this.businessObjectService; 1809 } 1810 1811 @Override 1812 protected BusinessObjectAuthorizationService getBusinessObjectAuthorizationService() { 1813 if (businessObjectAuthorizationService == null) { 1814 businessObjectAuthorizationService = KNSServiceLocatorWeb.getBusinessObjectAuthorizationService(); 1815 } 1816 return businessObjectAuthorizationService; 1817 } 1818 1819 public BusinessObjectMetaDataService getBusinessObjectMetaDataService() { 1820 if (businessObjectMetaDataService == null) { 1821 businessObjectMetaDataService = KNSServiceLocatorWeb.getBusinessObjectMetaDataService(); 1822 } 1823 return this.businessObjectMetaDataService; 1824 } 1825 1826 public EntityManagerFactory getEntityManagerFactory() { 1827 if (entityManagerFactory == null) { 1828 entityManagerFactory = KNSServiceLocator.getApplicationEntityManagerFactory(); 1829 } 1830 return this.entityManagerFactory; 1831 } 1832 1833 /** 1834 * @see org.kuali.rice.kns.web.struts.action.KualiAction#hideAllTabs(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 1835 */ 1836 @Override 1837 public ActionForward hideAllTabs(ActionMapping mapping, ActionForm form, 1838 HttpServletRequest request, HttpServletResponse response) 1839 throws Exception { 1840 if (form instanceof KualiDocumentFormBase) { 1841 WebUtils.reuseErrorMapFromPreviousRequest((KualiDocumentFormBase) form); 1842 } 1843 return super.hideAllTabs(mapping, form, request, response); 1844 } 1845 1846 /** 1847 * @see org.kuali.rice.kns.web.struts.action.KualiAction#showAllTabs(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 1848 */ 1849 @Override 1850 public ActionForward showAllTabs(ActionMapping mapping, ActionForm form, 1851 HttpServletRequest request, HttpServletResponse response) 1852 throws Exception { 1853 if (form instanceof KualiDocumentFormBase) { 1854 WebUtils.reuseErrorMapFromPreviousRequest((KualiDocumentFormBase) form); 1855 } 1856 return super.showAllTabs(mapping, form, request, response); 1857 } 1858 1859 /** 1860 * @see org.kuali.rice.kns.web.struts.action.KualiAction#toggleTab(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 1861 */ 1862 @Override 1863 public ActionForward toggleTab(ActionMapping mapping, ActionForm form, 1864 HttpServletRequest request, HttpServletResponse response) 1865 throws Exception { 1866 if (form instanceof KualiDocumentFormBase) { 1867 WebUtils.reuseErrorMapFromPreviousRequest((KualiDocumentFormBase) form); 1868 } 1869 return super.toggleTab(mapping, form, request, response); 1870 } 1871 1872 @Override 1873 protected void doProcessingAfterPost(KualiForm form, HttpServletRequest request) { 1874 super.doProcessingAfterPost(form, request); 1875 if (form instanceof KualiDocumentFormBase) { 1876 Document document = ((KualiDocumentFormBase) form).getDocument(); 1877 1878 getBusinessObjectService().linkUserFields(document); 1879 } 1880 } 1881 } 1882