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