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