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