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