View Javadoc

1   /*
2    * Copyright 2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 1.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl1.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.web.controller;
17  
18  import org.apache.commons.lang.ArrayUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.api.util.RiceKeyConstants;
21  import org.kuali.rice.core.framework.parameter.ParameterConstants;
22  import org.kuali.rice.core.framework.services.CoreFrameworkServiceLocator;
23  import org.kuali.rice.kew.api.WorkflowDocument;
24  import org.kuali.rice.kew.exception.WorkflowException;
25  import org.kuali.rice.kew.util.KEWConstants;
26  import org.kuali.rice.kim.api.identity.Person;
27  import org.kuali.rice.krad.bo.AdHocRouteRecipient;
28  import org.kuali.rice.krad.bo.Attachment;
29  import org.kuali.rice.krad.bo.DocumentHeader;
30  import org.kuali.rice.krad.bo.Note;
31  import org.kuali.rice.krad.document.Document;
32  import org.kuali.rice.krad.document.MaintenanceDocument;
33  import org.kuali.rice.krad.document.authorization.DocumentAuthorizer;
34  import org.kuali.rice.krad.exception.DocumentAuthorizationException;
35  import org.kuali.rice.krad.exception.UnknownDocumentIdException;
36  import org.kuali.rice.krad.exception.ValidationException;
37  import org.kuali.rice.krad.question.ConfirmationQuestion;
38  import org.kuali.rice.krad.rule.event.AddNoteEvent;
39  import org.kuali.rice.krad.service.AttachmentService;
40  import org.kuali.rice.krad.service.BusinessObjectService;
41  import org.kuali.rice.krad.service.DataDictionaryService;
42  import org.kuali.rice.krad.service.DocumentHelperService;
43  import org.kuali.rice.krad.service.DocumentService;
44  import org.kuali.rice.krad.service.KRADServiceLocator;
45  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
46  import org.kuali.rice.krad.service.NoteService;
47  import org.kuali.rice.krad.uif.UifParameters;
48  import org.kuali.rice.krad.uif.container.CollectionGroup;
49  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
50  import org.kuali.rice.krad.uif.view.View;
51  import org.kuali.rice.krad.uif.view.ViewIndex;
52  import org.kuali.rice.krad.util.GlobalVariables;
53  import org.kuali.rice.krad.util.KRADConstants;
54  import org.kuali.rice.krad.util.KRADPropertyConstants;
55  import org.kuali.rice.krad.util.KRADUtils;
56  import org.kuali.rice.krad.util.NoteType;
57  import org.kuali.rice.krad.util.SessionTicket;
58  import org.kuali.rice.krad.web.form.DocumentFormBase;
59  import org.kuali.rice.krad.web.form.UifFormBase;
60  import org.springframework.util.FileCopyUtils;
61  import org.springframework.validation.BindingResult;
62  import org.springframework.web.bind.ServletRequestBindingException;
63  import org.springframework.web.bind.annotation.ModelAttribute;
64  import org.springframework.web.bind.annotation.RequestMapping;
65  import org.springframework.web.bind.annotation.RequestMethod;
66  import org.springframework.web.multipart.MultipartFile;
67  import org.springframework.web.servlet.ModelAndView;
68  
69  import javax.servlet.http.HttpServletRequest;
70  import javax.servlet.http.HttpServletResponse;
71  
72  import java.io.FileNotFoundException;
73  import java.io.IOException;
74  import java.io.InputStream;
75  import java.util.ArrayList;
76  import java.util.HashMap;
77  import java.util.Map;
78  
79  /**
80   * Base controller class for all KRAD <code>DocumentView</code> screens working
81   * with <code>Document</code> models
82   * 
83   * <p>
84   * Provides default controller implementations for the standard document actions
85   * including: doc handler (retrieve from doc search and action list), save,
86   * route (and other KEW actions)
87   * </p>
88   * 
89   * @author Kuali Rice Team (rice.collab@kuali.org)
90   */
91  public abstract class DocumentControllerBase extends UifControllerBase {
92  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentControllerBase.class);
93  
94  	// COMMAND constants which cause docHandler to load an existing document
95  	// instead of creating a new one
96  	protected static final String[] DOCUMENT_LOAD_COMMANDS = { KEWConstants.ACTIONLIST_COMMAND,
97  			KEWConstants.DOCSEARCH_COMMAND, KEWConstants.SUPERUSER_COMMAND, KEWConstants.HELPDESK_ACTIONLIST_COMMAND };
98  
99  	private BusinessObjectService businessObjectService;
100 	private DataDictionaryService dataDictionaryService;
101 	private DocumentService documentService;
102 	private DocumentHelperService documentHelperService;
103 	private AttachmentService attachmentService;
104     private NoteService noteService;
105 
106 	@Override
107 	public abstract DocumentFormBase createInitialForm(HttpServletRequest request);
108 
109 	/**
110 	 * Used to funnel all document handling through, we could do useful things
111 	 * like log and record various openings and status Additionally it may be
112 	 * nice to have a single dispatcher that can know how to dispatch to a
113 	 * redirect url for document specific handling but we may not need that as
114 	 * all we should need is the document to be able to load itself based on
115 	 * document id and then which action forward or redirect is pertinent for
116 	 * the document type.
117 	 */
118 	@RequestMapping(params = "methodToCall=docHandler")
119 	public ModelAndView docHandler(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, HttpServletRequest request,
120 			HttpServletResponse response) throws Exception {
121 		String command = form.getCommand();
122 
123 		// in all of the following cases we want to load the document
124 		if (ArrayUtils.contains(DOCUMENT_LOAD_COMMANDS, command) && form.getDocId() != null) {
125 			loadDocument(form);
126 		}
127 		else if (KEWConstants.INITIATE_COMMAND.equals(command)) {
128 			createDocument(form);
129 		}
130 		else {
131 			LOG.error("docHandler called with invalid parameters");
132 			throw new IllegalStateException("docHandler called with invalid parameters");
133 		}
134 
135 		// TODO: authorization on document actions
136 		// if (KEWConstants.SUPERUSER_COMMAND.equalsIgnoreCase(command)) {
137 		// form.setSuppressAllButtons(true);
138 		// }
139 
140 		return getUIFModelAndView(form);
141 	}
142 
143 	/**
144 	 * Loads the document by its provided document header id. This has been
145 	 * abstracted out so that it can be overridden in children if the need
146 	 * arises.
147 	 * 
148 	 * @param form
149 	 *            - form instance that contains the doc id parameter and where
150 	 *            the retrieved document instance should be set
151 	 */
152 	protected void loadDocument(DocumentFormBase form) throws WorkflowException {
153 		String docId = form.getDocId();
154 
155 		Document doc = null;
156 		doc = getDocumentService().getByDocumentHeaderId(docId);
157 		if (doc == null) {
158 			throw new UnknownDocumentIdException(
159 					"Document no longer exists.  It may have been cancelled before being saved.");
160 		}
161 
162 		WorkflowDocument workflowDocument = doc.getDocumentHeader().getWorkflowDocument();
163 		if (!getDocumentHelperService().getDocumentAuthorizer(doc).canOpen(doc,
164 				GlobalVariables.getUserSession().getPerson())) {
165 			throw buildAuthorizationException("open", doc);
166 		}
167 
168 		// re-retrieve the document using the current user's session - remove
169 		// the system user from the WorkflowDcument object
170 		if (workflowDocument != doc.getDocumentHeader().getWorkflowDocument()) {
171 			LOG.warn("Workflow document changed via canOpen check");
172 			doc.getDocumentHeader().setWorkflowDocument(workflowDocument);
173 		}
174 
175 		form.setDocument(doc);
176 		WorkflowDocument workflowDoc = doc.getDocumentHeader().getWorkflowDocument();
177 		form.setDocTypeName(workflowDoc.getDocumentTypeName());
178 
179 		KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(), workflowDoc);
180 	}
181 
182 	/**
183 	 * Creates a new document of the type specified by the docTypeName property
184 	 * of the given form. This has been abstracted out so that it can be
185 	 * overridden in children if the need arises.
186 	 * 
187 	 * @param form
188 	 *            - form instance that contains the doc type parameter and where
189 	 *            the new document instance should be set
190 	 */
191 	protected void createDocument(DocumentFormBase form) throws WorkflowException {
192 		Document doc = getDocumentService().getNewDocument(form.getDocTypeName());
193 
194 		form.setDocument(doc);
195 		form.setDocTypeName(doc.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
196 	}
197 
198 	/**
199 	 * Saves the <code>Document</code> instance
200 	 */
201 	@RequestMapping(params = "methodToCall=save")
202 	public ModelAndView save(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, HttpServletRequest request,
203 			HttpServletResponse response) throws Exception {
204 
205 		doProcessingAfterPost(form, request);
206 
207 		// get any possible changes to to adHocWorkgroups
208 		// TODO turn this back on
209 		// refreshAdHocRoutingWorkgroupLookups(request, form);
210 
211 		Document document = form.getDocument();
212 
213 		String viewName = checkAndWarnAboutSensitiveData(form, request, response,
214 				KRADPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "save", "");
215 		// TODO if the happens we may need to save form to session or account
216 		// for it
217 		if (viewName != null) {
218 			return new ModelAndView(viewName);
219 		}
220 
221 		try {
222     		// save in workflow
223     		getDocumentService().saveDocument(document);
224 
225             // TODO: should add message to message map
226     		//GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_SAVED);
227     		form.setAnnotation("");
228 		}
229 		catch(ValidationException vex) {
230 		    // if errors in map, swallow exception so screen will draw with errors
231 			// if not then throw runtime because something bad happened
232 			if(GlobalVariables.getMessageMap().hasNoErrors()) {
233 				throw new RuntimeException("Validation Exception with no error message.", vex);
234 			}
235 		}
236 
237 		return getUIFModelAndView(form);
238 	}
239 
240 	/**
241 	 * Routes the <code>Document</code> instance using the document service
242 	 */
243 	@RequestMapping(params = "methodToCall=route")
244 	public ModelAndView route(@ModelAttribute("KualiForm") DocumentFormBase form, BindingResult result, HttpServletRequest request,
245 			HttpServletResponse response) throws Exception {
246 		doProcessingAfterPost(form, request);
247 
248 		// TODO: prerules
249 		// kualiDocumentFormBase.setDerivedValuesOnForm(request);
250 		// ActionForward preRulesForward = promptBeforeValidation(mapping, form,
251 		// request, response);
252 		// if (preRulesForward != null) {
253 		// return preRulesForward;
254 		// }
255 
256 		Document document = form.getDocument();
257 
258 		String viewName = checkAndWarnAboutSensitiveData(form, request, response,
259 				KRADPropertyConstants.DOCUMENT_EXPLANATION, document.getDocumentHeader().getExplanation(), "route", "");
260 		if (viewName != null) {
261 			return new ModelAndView(viewName);
262 		}
263 
264 		// TODO: adhoc recipients
265 		// getDocumentService().routeDocument(document, form.getAnnotation(),
266 		// combineAdHocRecipients(kualiDocumentFormBase));
267 		getDocumentService().routeDocument(document, form.getAnnotation(), new ArrayList<AdHocRouteRecipient>());
268 
269         // TODO: should added message to message map
270 		//GlobalVariables.getMessageList().add(RiceKeyConstants.MESSAGE_ROUTE_SUCCESSFUL);
271 		form.setAnnotation("");
272 
273 		// GlobalVariables.getUserSession().addObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_COMPLETE_OBJECT_KEY,Boolean.TRUE);
274 		return getUIFModelAndView(form);
275 	}
276 
277     
278     /**
279      * Called by the add note action for adding a note. Method
280      * validates, saves attachment and adds the time stamp and author.
281      * Calls the UifControllerBase.addLine method to handle 
282      * generic actions.
283      *
284      */
285     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=insertNote")
286     public ModelAndView insertNote(@ModelAttribute("KualiForm") UifFormBase uifForm,
287             BindingResult result, HttpServletRequest request, HttpServletResponse response) {
288 
289         // Get the note add line
290         String selectedCollectionPath = uifForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH);
291         CollectionGroup collectionGroup = uifForm.getPreviousView().getViewIndex()
292                 .getCollectionGroupByPath(selectedCollectionPath);
293         String addLinePath = collectionGroup.getAddLineBindingInfo().getBindingPath();
294         Object addLine = ObjectPropertyUtils.getPropertyValue(uifForm, addLinePath);
295         Note newNote = (Note) addLine;
296         newNote.setNotePostedTimestampToCurrent();
297 
298         Document document = ((DocumentFormBase) uifForm).getDocument();
299 
300         newNote.setRemoteObjectIdentifier(document.getNoteTarget().getObjectId());
301 
302         // Get the attachment file
303         String attachmentTypeCode = null;
304         MultipartFile attachmentFile = uifForm.getAttachmentFile();
305         Attachment attachment = null;
306         if (attachmentFile != null && !StringUtils.isBlank(attachmentFile.getOriginalFilename())) {
307             if (attachmentFile.getSize() == 0) {
308                 GlobalVariables.getMessageMap().putError(
309                         String.format("%s.%s",
310                                 KRADConstants.NEW_DOCUMENT_NOTE_PROPERTY_NAME,
311                                 KRADConstants.NOTE_ATTACHMENT_FILE_PROPERTY_NAME),
312                         RiceKeyConstants.ERROR_UPLOADFILE_EMPTY,
313                         attachmentFile.getOriginalFilename());
314             } else {
315                 if (newNote.getAttachment() != null) {
316                     attachmentTypeCode = newNote.getAttachment().getAttachmentTypeCode();
317                 }
318 
319                 DocumentAuthorizer documentAuthorizer = KRADServiceLocatorWeb.getDocumentHelperService().getDocumentAuthorizer(
320                         document);
321                 if (!documentAuthorizer.canAddNoteAttachment(document, attachmentTypeCode, GlobalVariables.getUserSession()
322                         .getPerson())) {
323                     throw buildAuthorizationException("annotate", document);
324                 }                
325                 try {
326                     String attachmentType = null;
327                     Attachment newAttachment = newNote.getAttachment();
328                     if (newAttachment != null) {
329                         attachmentType = newAttachment.getAttachmentTypeCode();
330                     }
331                     attachment = getAttachmentService().createAttachment(document.getNoteTarget(),
332                             attachmentFile.getOriginalFilename(), attachmentFile.getContentType(),
333                             (int) attachmentFile.getSize(), attachmentFile.getInputStream(), attachmentType);
334                 } catch (IOException e) {
335                     e.printStackTrace();
336                 }
337             }
338         }
339 
340         Person kualiUser = GlobalVariables.getUserSession().getPerson();
341         if (kualiUser == null) {
342             throw new IllegalStateException("Current UserSession has a null Person.");
343         }
344 
345         newNote.setAuthorUniversalIdentifier(kualiUser.getPrincipalId());
346 
347         // validate the note
348         boolean rulePassed = KRADServiceLocatorWeb.getKualiRuleService()
349                 .applyRules(new AddNoteEvent(document, newNote));
350 
351         // if the rule evaluation passed, let's add the note
352         if (rulePassed) {
353             newNote.refresh();
354 
355             DocumentHeader documentHeader = document.getDocumentHeader();
356 
357             // adding the attachment after refresh gets called, since the attachment record doesn't get persisted
358             // until the note does (and therefore refresh doesn't have any attachment to autoload based on the id, nor does it
359             // autopopulate the id since the note hasn't been persisted yet)
360             if (attachment != null) {
361                 newNote.addAttachment(attachment);
362             }
363             // Save the note if the document is already saved
364             if (!documentHeader.getWorkflowDocument().isInitiated()
365                         && StringUtils.isNotEmpty(document.getNoteTarget().getObjectId())
366                         && !(document instanceof MaintenanceDocument && NoteType.BUSINESS_OBJECT.getCode().equals(
367                                 newNote.getNoteTypeCode()))) {
368 
369                 getNoteService().save(newNote);
370             }
371 
372         }    
373         
374         return addLine(uifForm, result, request, response);
375     }    
376     
377     /**
378      * Called by the delete note action for deleting a note. 
379      * Calls the UifControllerBase.deleteLine method to handle 
380      * generic actions.
381      * 
382      */
383     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=deleteNote")
384     public ModelAndView deleteNote(@ModelAttribute("KualiForm") UifFormBase uifForm,
385             BindingResult result, HttpServletRequest request, HttpServletResponse response) {
386         
387         String selectedLineIndex = uifForm.getActionParamaterValue("selectedLineIndex");
388         Document document = ((DocumentFormBase)uifForm).getDocument();
389         Note note = document.getNote(Integer.parseInt(selectedLineIndex));
390         
391         Attachment attachment = note.getAttachment();
392         String attachmentTypeCode = null;
393         if (attachment != null) {
394             attachmentTypeCode = attachment.getAttachmentTypeCode();
395         }
396         
397         String authorUniversalIdentifier = note.getAuthorUniversalIdentifier();
398         if (!KRADUtils.canDeleteNoteAttachment(document, attachmentTypeCode, authorUniversalIdentifier)) {
399             throw buildAuthorizationException("annotate", document);
400         }
401 
402         if (attachment != null && attachment.isComplete()) { // only do this if the note has been persisted
403             //KFSMI-798 - refresh() changed to refreshNonUpdateableReferences()
404             //All references for the business object Attachment are auto-update="none",
405             //so refreshNonUpdateableReferences() should work the same as refresh()
406             if (note.getNoteIdentifier() != null) { // KULRICE-2343 don't blow away note reference if the note wasn't persisted
407                 attachment.refreshNonUpdateableReferences();
408             }
409             getAttachmentService().deleteAttachmentContents(attachment);
410         }
411         // delete the note if the document is already saved
412         if (!document.getDocumentHeader().getWorkflowDocument().isInitiated()) {
413             getNoteService().deleteNote(note);
414         }
415 
416         return deleteLine(uifForm, result, request, response);
417     }     
418     
419     /**
420      * Called by the download attachment action on a note. Method
421      * gets the attachment input stream from the AttachmentService
422      * and writes it to the request output stream.
423      * 
424      */
425     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=downloadAttachment")
426     public ModelAndView downloadAttachment(@ModelAttribute("KualiForm") UifFormBase uifForm,
427             BindingResult result, HttpServletRequest request, HttpServletResponse response) throws ServletRequestBindingException, FileNotFoundException, IOException {
428         // Get the attachment input stream
429         String selectedLineIndex = uifForm.getActionParamaterValue("selectedLineIndex");
430         Note note = ((DocumentFormBase)uifForm).getDocument().getNote(Integer.parseInt(selectedLineIndex));
431         Attachment attachment = note.getAttachment();
432         InputStream is = getAttachmentService().retrieveAttachmentContents(attachment);
433         
434         // Set the response headers
435         response.setContentType(attachment.getAttachmentMimeTypeCode());
436         response.setContentLength(attachment.getAttachmentFileSize().intValue());
437         response.setHeader("Expires", "0");
438         response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
439         response.setHeader("Pragma", "public");
440         response.setHeader("Content-Disposition","attachment; filename=\"" + attachment.getAttachmentFileName() + "\"");
441         
442         // Copy the input stream to the response
443         FileCopyUtils.copy(is, response.getOutputStream()); 
444         return null;
445     }     
446 
447     /**
448      * Called by the cancel attachment action on a note. Method
449      * removes the attachment file from the form.
450      * 
451      */
452     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=cancelAttachment")
453     public ModelAndView cancelAttachment(@ModelAttribute("KualiForm") UifFormBase uifForm,
454             BindingResult result, HttpServletRequest request, HttpServletResponse response) {
455         // Remove the attached file
456         uifForm.setAttachmentFile(null);
457         return getUIFModelAndView(uifForm);
458     }
459     	
460 	
461 	
462 	
463 	/**
464 	 * Does all special processing on a document that should happen on each HTTP
465 	 * post (ie, save, route, approve, etc).
466 	 * 
467 	 * @param form
468 	 * @param request
469 	 */
470 	protected void doProcessingAfterPost(DocumentFormBase form, HttpServletRequest request) {
471 		getBusinessObjectService().linkUserFields(form.getDocument());
472 	}
473 
474 	// TODO this needs more analysis before porting can finish
475 	/*
476 	 * protected void refreshAdHocRoutingWorkgroupLookups(HttpServletRequest
477 	 * request, DocumentFormBase form) throws WorkflowException { for
478 	 * (Enumeration<String> i = request.getParameterNames();
479 	 * i.hasMoreElements();) { String parameterName = i.nextElement();
480 	 * 
481 	 * // TODO does this really belong in the loop if
482 	 * (parameterName.equals("newAdHocRouteWorkgroup.recipientName") &&
483 	 * !"".equals(request.getParameter(parameterName))) { //check for namespace
484 	 * String namespace = KimApiConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE; if
485 	 * (request.getParameter("newAdHocRouteWorkgroup.recipientNamespaceCode") !=
486 	 * null &&
487 	 * !"".equals(request.getParameter("newAdHocRouteWorkgroup.recipientName"
488 	 * ).trim())) {
489 	 * 
490 	 * namespace =
491 	 * request.getParameter("newAdHocRouteWorkgroup.recipientNamespaceCode"
492 	 * ).trim(); } Group group =
493 	 * getIdentityManagementService().getGroupByName(namespace,
494 	 * request.getParameter(parameterName)); if (group != null) {
495 	 * form.getNewAdHocRouteWorkgroup().setId(group.getGroupId());
496 	 * form.getNewAdHocRouteWorkgroup().setRecipientName(group.getGroupName());
497 	 * form
498 	 * .getNewAdHocRouteWorkgroup().setRecipientNamespaceCode(group.getNamespaceCode
499 	 * ()); } else { throw new
500 	 * RuntimeException("Invalid workgroup id passed as parameter."); } }
501 	 * 
502 	 * // TODO need to look at screen, will most of this just be bound to the
503 	 * form by spring? if (parameterName.startsWith("adHocRouteWorkgroup[") &&
504 	 * !"".equals(request.getParameter(parameterName))) { if
505 	 * (parameterName.endsWith(".recipientName")) { int lineNumber =
506 	 * Integer.parseInt(StringUtils.substringBetween(parameterName, "[", "]"));
507 	 * //check for namespace String namespaceParam = "adHocRouteWorkgroup[" +
508 	 * lineNumber + "].recipientNamespaceCode"; String namespace =
509 	 * KimApiConstants.KIM_GROUP_DEFAULT_NAMESPACE_CODE; if
510 	 * (request.getParameter(namespaceParam) != null &&
511 	 * !"".equals(request.getParameter(namespaceParam).trim())) { namespace =
512 	 * request.getParameter(namespaceParam).trim(); } Group group =
513 	 * getIdentityManagementService().getGroupByName(namespace,
514 	 * request.getParameter(parameterName)); if (group != null) {
515 	 * form.getAdHocRouteWorkgroup(lineNumber).setId(group.getGroupId());
516 	 * form.getAdHocRouteWorkgroup
517 	 * (lineNumber).setRecipientName(group.getGroupName());
518 	 * form.getAdHocRouteWorkgroup
519 	 * (lineNumber).setRecipientNamespaceCode(group.getNamespaceCode()); } else
520 	 * { throw new
521 	 * RuntimeException("Invalid workgroup id passed as parameter."); } } } } }
522 	 */
523 
524 	/**
525 	 * Checks if the given value matches patterns that indicate sensitive data
526 	 * and if configured to give a warning for sensitive data will prompt the
527 	 * user to continue.
528 	 * 
529 	 * @param form
530 	 * @param request
531 	 * @param response
532 	 * @param fieldName
533 	 *            - name of field with value being checked
534 	 * @param fieldValue
535 	 *            - value to check for sensitive data
536 	 * @param caller
537 	 *            - method that should be called back from question
538 	 * @param context
539 	 *            - additional context that needs to be passed back with the
540 	 *            question response
541 	 * @return - view for spring to forward to, or null if processing should
542 	 *         continue
543 	 * @throws Exception
544 	 */
545 	protected String checkAndWarnAboutSensitiveData(DocumentFormBase form, HttpServletRequest request,
546 			HttpServletResponse response, String fieldName, String fieldValue, String caller, String context)
547 			throws Exception {
548 
549 		String viewName = null;
550 		Document document = form.getDocument();
551 
552         // TODO: need to move containsSensitiveDataPatternMatch to util class in krad
553         boolean containsSensitiveData = false;
554 		//boolean containsSensitiveData = WebUtils.containsSensitiveDataPatternMatch(fieldValue);
555 
556 		// check if warning is configured in which case we will prompt, or if
557 		// not business rules will thrown an error
558 		boolean warnForSensitiveData = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean(
559 				KRADConstants.KRAD_NAMESPACE, ParameterConstants.ALL_COMPONENT,
560 				KRADConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS_WARNING_IND);
561 
562 		// determine if the question has been asked yet
563 		Map<String, String> ticketContext = new HashMap<String, String>();
564 		ticketContext.put(KRADPropertyConstants.DOCUMENT_NUMBER, document.getDocumentNumber());
565 		ticketContext.put(KRADConstants.CALLING_METHOD, caller);
566 		ticketContext.put(KRADPropertyConstants.NAME, fieldName);
567 
568 		boolean questionAsked = GlobalVariables.getUserSession().hasMatchingSessionTicket(
569 				KRADConstants.SENSITIVE_DATA_QUESTION_SESSION_TICKET, ticketContext);
570 
571 		// start in logic for confirming the sensitive data
572 		if (containsSensitiveData && warnForSensitiveData && !questionAsked) {
573 			Object question = request.getParameter(KRADConstants.QUESTION_INST_ATTRIBUTE_NAME);
574 			if (question == null || !KRADConstants.DOCUMENT_SENSITIVE_DATA_QUESTION.equals(question)) {
575 
576 				// TODO not ready for question framework yet
577 				/*
578 				 * // question hasn't been asked, prompt to continue return
579 				 * this.performQuestionWithoutInput(mapping, form, request,
580 				 * response, KRADConstants.DOCUMENT_SENSITIVE_DATA_QUESTION,
581 				 * getKualiConfigurationService()
582 				 * .getPropertyValueAsString(RiceKeyConstants
583 				 * .QUESTION_SENSITIVE_DATA_DOCUMENT),
584 				 * KRADConstants.CONFIRMATION_QUESTION, caller, context);
585 				 */
586 				viewName = "ask_user_questions";
587 			}
588 			else {
589 				Object buttonClicked = request.getParameter(KRADConstants.QUESTION_CLICKED_BUTTON);
590 
591 				// if no button clicked just reload the doc
592 				if (ConfirmationQuestion.NO.equals(buttonClicked)) {
593 					// TODO figure out what to return
594 					viewName = "user_says_no";
595 				}
596 
597 				// answered yes, create session ticket so we not to ask question
598 				// again if there are further question requests
599 				SessionTicket ticket = new SessionTicket(KRADConstants.SENSITIVE_DATA_QUESTION_SESSION_TICKET);
600 				ticket.setTicketContext(ticketContext);
601 				GlobalVariables.getUserSession().putSessionTicket(ticket);
602 			}
603 		}
604 
605 		// returning null will indicate processing should continue (no redirect)
606 		return viewName;
607 	}
608 
609 	/**
610 	 * Convenience method for building authorization exceptions
611 	 * 
612 	 * @param action
613 	 *            - the action that was requested
614 	 * @param document
615 	 *            - document instance the action was requested for
616 	 */
617 	protected DocumentAuthorizationException buildAuthorizationException(String action, Document document) {
618 		return new DocumentAuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(),
619 				action, document.getDocumentNumber());
620 	}
621 
622 	public BusinessObjectService getBusinessObjectService() {
623 		if (this.businessObjectService == null) {
624 			this.businessObjectService = KRADServiceLocator.getBusinessObjectService();
625 		}
626 		return this.businessObjectService;
627 	}
628 
629 	public void setBusinessObjectService(BusinessObjectService businessObjectService) {
630 		this.businessObjectService = businessObjectService;
631 	}
632 
633 	public DataDictionaryService getDataDictionaryService() {
634 		if (this.dataDictionaryService == null) {
635 			this.dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
636 		}
637 		return this.dataDictionaryService;
638 	}
639 
640 	public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
641 		this.dataDictionaryService = dataDictionaryService;
642 	}
643 
644 	public DocumentService getDocumentService() {
645 		if (this.documentService == null) {
646 			this.documentService = KRADServiceLocatorWeb.getDocumentService();
647 		}
648 		return this.documentService;
649 	}
650 
651 	public void setDocumentService(DocumentService documentService) {
652 		this.documentService = documentService;
653 	}
654 
655 	public DocumentHelperService getDocumentHelperService() {
656 		if (this.documentHelperService == null) {
657 			this.documentHelperService = KRADServiceLocatorWeb.getDocumentHelperService();
658 		}
659 		return this.documentHelperService;
660 	}
661 
662 	public void setDocumentHelperService(DocumentHelperService documentHelperService) {
663 		this.documentHelperService = documentHelperService;
664 	}
665 	
666 	public AttachmentService getAttachmentService() {
667         if (attachmentService == null) {
668             attachmentService = KRADServiceLocator.getAttachmentService();
669         }
670         return this.attachmentService;
671     }
672 
673 	public NoteService getNoteService() {
674         if (noteService == null) {
675             noteService = KRADServiceLocator.getNoteService();
676         }
677         return this.noteService;
678     }	
679 }