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