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