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