View Javadoc

1   /**
2    * Copyright 2005-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.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/ecl2.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.kns.util;
17  
18  import java.io.ByteArrayOutputStream;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.OutputStream;
22  import java.util.Arrays;
23  import java.util.Collection;
24  import java.util.Enumeration;
25  import java.util.HashMap;
26  import java.util.Hashtable;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  import java.util.regex.Matcher;
32  import java.util.regex.Pattern;
33  
34  import javax.servlet.ServletException;
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  import javax.servlet.http.HttpSession;
38  import javax.servlet.jsp.PageContext;
39  
40  import org.apache.commons.lang.StringEscapeUtils;
41  import org.apache.commons.lang.StringUtils;
42  import org.apache.log4j.Level;
43  import org.apache.log4j.Logger;
44  import org.apache.struts.Globals;
45  import org.apache.struts.action.ActionForm;
46  import org.apache.struts.action.ActionMapping;
47  import org.apache.struts.action.ActionServletWrapper;
48  import org.apache.struts.upload.CommonsMultipartRequestHandler;
49  import org.apache.struts.upload.FormFile;
50  import org.apache.struts.upload.MultipartRequestHandler;
51  import org.apache.struts.upload.MultipartRequestWrapper;
52  import org.kuali.rice.core.api.config.property.ConfigurationService;
53  import org.kuali.rice.core.api.util.RiceKeyConstants;
54  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
55  import org.kuali.rice.kns.datadictionary.KNSDocumentEntry;
56  import org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry;
57  import org.kuali.rice.kns.document.authorization.DocumentAuthorizer;
58  import org.kuali.rice.kns.service.KNSServiceLocator;
59  import org.kuali.rice.kns.web.struts.action.KualiMultipartRequestHandler;
60  import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
61  import org.kuali.rice.kns.web.struts.form.KualiForm;
62  import org.kuali.rice.kns.web.struts.form.KualiMaintenanceForm;
63  import org.kuali.rice.kns.web.struts.form.pojo.PojoFormBase;
64  import org.kuali.rice.krad.datadictionary.AttributeDefinition;
65  import org.kuali.rice.krad.datadictionary.AttributeSecurity;
66  import org.kuali.rice.krad.datadictionary.DataDictionary;
67  import org.kuali.rice.krad.datadictionary.DataDictionaryEntryBase;
68  import org.kuali.rice.krad.datadictionary.mask.MaskFormatter;
69  import org.kuali.rice.krad.document.Document;
70  import org.kuali.rice.krad.exception.ValidationException;
71  import org.kuali.rice.krad.service.KRADServiceLocator;
72  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
73  import org.kuali.rice.krad.util.GlobalVariables;
74  import org.kuali.rice.krad.util.KRADConstants;
75  import org.kuali.rice.krad.util.MessageMap;
76  import org.kuali.rice.krad.util.ObjectUtils;
77  
78  /**
79   * General helper methods for handling requests.
80   */
81  public class WebUtils {
82  	private static final Logger LOG = Logger.getLogger(WebUtils.class);
83  
84  	private static final String IMAGE_COORDINATE_CLICKED_X_EXTENSION = ".x";
85  	private static final String IMAGE_COORDINATE_CLICKED_Y_EXTENSION = ".y";
86  
87  	private static final String APPLICATION_IMAGE_URL_PROPERTY_PREFIX = "application.custom.image.url";
88  	private static final String DEFAULT_IMAGE_URL_PROPERTY_NAME = "kr.externalizable.images.url";
89  
90  	/**
91  	 * A request attribute name that indicates that a
92  	 * {@link org.kuali.rice.kns.exception.FileUploadLimitExceededException} has already been thrown for the
93  	 * request.
94  	 */
95  	public static final String FILE_UPLOAD_LIMIT_EXCEEDED_EXCEPTION_ALREADY_THROWN = "fileUploadLimitExceededExceptionAlreadyThrown";
96  
97  	private static ConfigurationService configurationService;
98  
99  	/**
100 	 * Checks for methodToCall parameter, and picks off the value using set dot
101 	 * notation. Handles the problem of image submits.
102 	 * 
103 	 * @param request
104 	 * @return methodToCall String
105 	 */
106 	public static String parseMethodToCall(ActionForm form, HttpServletRequest request) {
107 		String methodToCall = null;
108 
109 		// check if is specified cleanly
110 		if (StringUtils.isNotBlank(request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER))) {
111 			if (form instanceof KualiForm
112 					&& !((KualiForm) form).shouldMethodToCallParameterBeUsed(KRADConstants.DISPATCH_REQUEST_PARAMETER,
113 							request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER), request)) {
114 				throw new RuntimeException("Cannot verify that the methodToCall should be "
115 						+ request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER));
116 			}
117 			methodToCall = request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER);
118 			// include .x at the end of the parameter to make it consistent w/
119 			// other parameters
120 			request.setAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE, KRADConstants.DISPATCH_REQUEST_PARAMETER + "."
121 					+ methodToCall + IMAGE_COORDINATE_CLICKED_X_EXTENSION);
122 		}
123 
124 		/**
125 		 * The reason why we are checking for a ".x" at the end of the parameter
126 		 * name: It is for the image names that in addition to sending the form
127 		 * data, the web browser sends the x,y coordinate of where the user
128 		 * clicked on the image. If the image input is not given a name then the
129 		 * browser sends the x and y coordinates as the "x" and "y" input
130 		 * fields. If the input image does have a name, the x and y coordinates
131 		 * are sent using the format name.x and name.y.
132 		 */
133 		if (methodToCall == null) {
134 			// iterate through parameters looking for methodToCall
135 			for (Enumeration i = request.getParameterNames(); i.hasMoreElements();) {
136 				String parameterName = (String) i.nextElement();
137 
138 				// check if the parameter name is a specifying the methodToCall
139 				if (isMethodToCall(parameterName)) {
140 					methodToCall = getMethodToCallSettingAttribute(form, request, parameterName);
141 					break;
142 				}
143 				else {
144 					// KULRICE-1218: Check if the parameter's values match (not
145 					// just the name)
146 					for (String value : request.getParameterValues(parameterName)) {
147 						// adding period to startsWith check - don't want to get
148 						// confused with methodToCallFoobar
149 						if (isMethodToCall(value)) {
150 							methodToCall = getMethodToCallSettingAttribute(form, request, value);
151 							// why is there not a break outer loop here?
152 						}
153 					}
154 				}
155 			}
156 		}
157 
158 		return methodToCall;
159 	}
160 
161     /**
162 	 * Checks if a string signifies a methodToCall string
163 	 * 
164 	 * @param string
165 	 *            the string to check
166 	 * @return true if is a methodToCall
167 	 */
168 	private static boolean isMethodToCall(String string) {
169 		// adding period to startsWith check - don't want to get confused with
170 		// methodToCallFoobar
171 		return string.startsWith(KRADConstants.DISPATCH_REQUEST_PARAMETER + ".");
172 	}
173 
174 	/**
175 	 * Parses out the methodToCall command and also sets the request attribute
176 	 * for the methodToCall.
177 	 * 
178 	 * @param form
179 	 *            the ActionForm
180 	 * @param request
181 	 *            the request to set the attribute on
182 	 * @param string
183 	 *            the methodToCall string
184 	 * @return the methodToCall command
185 	 */
186 	private static String getMethodToCallSettingAttribute(ActionForm form, HttpServletRequest request, String string) {
187 
188 		if (form instanceof KualiForm
189 				&& !((KualiForm) form).shouldMethodToCallParameterBeUsed(string, request.getParameter(string), request)) {
190 			throw new RuntimeException("Cannot verify that the methodToCall should be " + string);
191 		}
192 		// always adding a coordinate even if not an image
193 		final String attributeValue = endsWithCoordinates(string) ? string : string
194 				+ IMAGE_COORDINATE_CLICKED_X_EXTENSION;
195 		final String methodToCall = StringUtils.substringBetween(attributeValue,
196 				KRADConstants.DISPATCH_REQUEST_PARAMETER + ".", ".");
197 		request.setAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE, attributeValue);
198 		return methodToCall;
199 	}
200 
201 	/**
202 	 * Iterates through and logs (at the given level) all attributes and
203 	 * parameters of the given request onto the given Logger
204 	 * 
205 	 * @param request
206 	 * @param logger
207 	 */
208 	public static void logRequestContents(Logger logger, Level level, HttpServletRequest request) {
209 		if (logger.isEnabledFor(level)) {
210 			logger.log(level, "--------------------");
211 			logger.log(level, "HttpRequest attributes:");
212 			for (Enumeration e = request.getAttributeNames(); e.hasMoreElements();) {
213 				String attrName = (String) e.nextElement();
214 				Object attrValue = request.getAttribute(attrName);
215 
216 				if (attrValue.getClass().isArray()) {
217 					logCollection(logger, level, attrName, Arrays.asList((Object[]) attrValue));
218 				}
219 				else if (attrValue instanceof Collection) {
220 					logCollection(logger, level, attrName, (Collection) attrValue);
221 				}
222 				else if (attrValue instanceof Map) {
223 					logMap(logger, level, attrName, (Map) attrValue);
224 				}
225 				else {
226 					logObject(logger, level, attrName, attrValue);
227 				}
228 			}
229 
230 			logger.log(level, "--------------------");
231 			logger.log(level, "HttpRequest parameters:");
232 			for (Enumeration i = request.getParameterNames(); i.hasMoreElements();) {
233 				String paramName = (String) i.nextElement();
234 				String[] paramValues = (String[]) request.getParameterValues(paramName);
235 
236 				logArray(logger, level, paramName, paramValues);
237 			}
238 
239 			logger.log(level, "--------------------");
240 		}
241 	}
242 
243 	private static void logArray(Logger logger, Level level, String arrayName, Object[] array) {
244 		StringBuffer value = new StringBuffer("[");
245 		for (int i = 0; i < array.length; ++i) {
246 			if (i > 0) {
247 				value.append(",");
248 			}
249 			value.append(array[i]);
250 		}
251 		value.append("]");
252 
253 		logThing(logger, level, arrayName, value);
254 	}
255 
256 	private static void logCollection(Logger logger, Level level, String collectionName, Collection c) {
257 		StringBuffer value = new StringBuffer("{");
258 		for (Iterator i = c.iterator(); i.hasNext();) {
259 			value.append(i.next());
260 			if (i.hasNext()) {
261 				value.append(",");
262 			}
263 		}
264 		value.append("}");
265 
266 		logThing(logger, level, collectionName, value);
267 	}
268 
269 	private static void logMap(Logger logger, Level level, String mapName, Map m) {
270 		StringBuffer value = new StringBuffer("{");
271 		for (Iterator i = m.entrySet().iterator(); i.hasNext();) {
272 			Map.Entry e = (Map.Entry) i.next();
273 			value.append("('" + e.getKey() + "','" + e.getValue() + "')");
274 		}
275 		value.append("}");
276 
277 		logThing(logger, level, mapName, value);
278 	}
279 
280 	private static void logObject(Logger logger, Level level, String objectName, Object o) {
281 		logThing(logger, level, objectName, "'" + o + "'");
282 	}
283 
284 	private static void logThing(Logger logger, Level level, String thingName, Object thing) {
285 		logger.log(level, "    '" + thingName + "' => " + thing);
286 	}
287 
288 	/**
289 	 * A file that is not of type text/plain or text/html can be output through
290 	 * the response using this method.
291 	 * 
292 	 * @param response
293 	 * @param contentType
294 	 * @param byteArrayOutputStream
295 	 * @param fileName
296 	 */
297 	public static void saveMimeOutputStreamAsFile(HttpServletResponse response, String contentType,
298 			ByteArrayOutputStream byteArrayOutputStream, String fileName) throws IOException {
299 
300 		// set response
301 		response.setContentType(contentType);
302 		response.setHeader("Content-disposition", "attachment; filename=" + fileName);
303 		response.setHeader("Expires", "0");
304 		response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
305 		response.setHeader("Pragma", "public");
306 		response.setContentLength(byteArrayOutputStream.size());
307 
308 		// write to output
309 		OutputStream outputStream = response.getOutputStream();
310 		byteArrayOutputStream.writeTo(response.getOutputStream());
311 		outputStream.flush();
312 		outputStream.close();
313 	}
314 
315 	/**
316 	 * A file that is not of type text/plain or text/html can be output through
317 	 * the response using this method.
318 	 * 
319 	 * @param response
320 	 * @param contentType
321 	 * @param inStream
322 	 * @param fileName
323 	 */
324 	public static void saveMimeInputStreamAsFile(HttpServletResponse response, String contentType,
325 			InputStream inStream, String fileName, int fileSize) throws IOException {
326 
327 		// set response
328 		response.setContentType(contentType);
329 		response.setHeader("Content-disposition", "attachment; filename=" + fileName);
330 		response.setHeader("Expires", "0");
331 		response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
332 		response.setHeader("Pragma", "public");
333 		response.setContentLength(fileSize);
334 
335 		// write to output
336 		OutputStream out = response.getOutputStream();
337 		while (inStream.available() > 0) {
338 			out.write(inStream.read());
339 		}
340 		out.flush();
341 	}
342 
343 	/**
344 	 * JSTL function to return the tab state of the tab from the form.
345 	 * 
346 	 * @param form
347 	 * @param tabKey
348 	 * @return
349 	 */
350 	public static String getTabState(KualiForm form, String tabKey) {
351 		return form.getTabState(tabKey);
352 	}
353 
354 	public static void incrementTabIndex(KualiForm form, String tabKey) {
355 		form.incrementTabIndex();
356 	}
357 
358 	/**
359 	 * Generates a String from the title that can be used as a Map key.
360 	 * 
361 	 * @param tabTitle
362 	 * @return
363 	 */
364 	public static String generateTabKey(String tabTitle) {
365 		String key = "";
366 		if (!StringUtils.isBlank(tabTitle)) {
367 			key = tabTitle.replaceAll("\\W", "");
368 			// if (key.length() > 25) {
369 			// key = key.substring(0, 24);
370 			// }
371 		}
372 
373 		return key;
374 	}
375 
376 	public static void getMultipartParameters(HttpServletRequest request, ActionServletWrapper servletWrapper,
377 			ActionForm form, ActionMapping mapping) {
378 		Map params = new HashMap();
379 
380 		// Get the ActionServletWrapper from the form bean
381 		// ActionServletWrapper servletWrapper = getServletWrapper();
382 
383 		try {
384 			CommonsMultipartRequestHandler multipartHandler = new CommonsMultipartRequestHandler();
385 			if (multipartHandler != null) {
386 				// Set servlet and mapping info
387 				if (servletWrapper != null) {
388 					// from pojoformbase
389 					// servlet only affects tempdir on local disk
390 					servletWrapper.setServletFor(multipartHandler);
391 				}
392 				multipartHandler.setMapping((ActionMapping) request.getAttribute(Globals.MAPPING_KEY));
393 				// Initialize multipart request class handler
394 				multipartHandler.handleRequest(request);
395 
396 				Collection<FormFile> files = multipartHandler.getFileElements().values();
397 				Enumeration keys = multipartHandler.getFileElements().keys();
398 
399 				while (keys.hasMoreElements()) {
400 					Object key = keys.nextElement();
401 					FormFile file = (FormFile) multipartHandler.getFileElements().get(key);
402 					long maxSize = WebUtils.getMaxUploadSize(form);
403 					if (LOG.isDebugEnabled()) {
404 						LOG.debug(file.getFileSize());
405 					}
406 					if (maxSize > 0 && Long.parseLong(file.getFileSize() + "") > maxSize) {
407 
408 						GlobalVariables.getMessageMap().putError(key.toString(),
409 								RiceKeyConstants.ERROR_UPLOADFILE_SIZE,
410 								new String[] { file.getFileName(), Long.toString(maxSize) });
411 
412 					}
413 				}
414 
415 				// get file elements for kualirequestprocessor
416 				if (servletWrapper == null) {
417 					request.setAttribute(KRADConstants.UPLOADED_FILE_REQUEST_ATTRIBUTE_KEY,
418 							getFileParametersForMultipartRequest(request, multipartHandler));
419 				}
420 			}
421 		}
422 		catch (ServletException e) {
423 			throw new ValidationException("unable to handle multipart request " + e.getMessage(), e);
424 		}
425 	}
426 
427 	public static long getMaxUploadSize(ActionForm form) {
428 		long max = 0L;
429 		KualiMultipartRequestHandler multipartHandler = new KualiMultipartRequestHandler();
430 		if (form instanceof PojoFormBase) {
431 			max = multipartHandler.calculateMaxUploadSizeToMaxOfList(((PojoFormBase) form).getMaxUploadSizes());
432 		}
433 		if (LOG.isDebugEnabled()) {
434 			LOG.debug("Max File Upload Size: " + max);
435 		}
436 		return max;
437 	}
438 
439 	private static Map getFileParametersForMultipartRequest(HttpServletRequest request,
440 			MultipartRequestHandler multipartHandler) {
441 		Map parameters = new HashMap();
442 		Hashtable elements = multipartHandler.getFileElements();
443 		Enumeration e = elements.keys();
444 		while (e.hasMoreElements()) {
445 			String key = (String) e.nextElement();
446 			parameters.put(key, elements.get(key));
447 		}
448 
449 		if (request instanceof MultipartRequestWrapper) {
450 			request = (HttpServletRequest) ((MultipartRequestWrapper) request).getRequest();
451 			e = request.getParameterNames();
452 			while (e.hasMoreElements()) {
453 				String key = (String) e.nextElement();
454 				parameters.put(key, request.getParameterValues(key));
455 			}
456 		}
457 		else {
458 			LOG.debug("Gathering multipart parameters for unwrapped request");
459 		}
460 		return parameters;
461 	}
462 
463 	// end multipart
464 
465 	public static void registerEditableProperty(PojoFormBase form, String editablePropertyName) {
466 		form.registerEditableProperty(editablePropertyName);
467 	}
468 
469 	public static boolean isDocumentSession(Document document, PojoFormBase docForm) {
470 		boolean sessionDoc = document instanceof org.kuali.rice.krad.document.SessionDocument;
471 		boolean dataDictionarySessionDoc = false;
472 		if (!sessionDoc) {
473 			DataDictionary dataDictionary = KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary();
474 			if (docForm instanceof KualiMaintenanceForm) {
475 				KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) docForm;
476 				if (dataDictionary != null) {
477 					if (maintenanceForm.getDocTypeName() != null) {
478                         MaintenanceDocumentEntry maintenanceDocumentEntry = (MaintenanceDocumentEntry) dataDictionary.getDocumentEntry(maintenanceForm.getDocTypeName());
479 						dataDictionarySessionDoc = maintenanceDocumentEntry.isSessionDocument();
480 					}
481 				}
482 			}
483 			else {
484 				if (document != null && dataDictionary != null) {
485 					KNSDocumentEntry documentEntry = (KNSDocumentEntry) dataDictionary.getDocumentEntry(document.getClass().getName());
486 					dataDictionarySessionDoc = documentEntry.isSessionDocument();
487 				}
488 			}
489 		}
490 		return sessionDoc || dataDictionarySessionDoc;
491 	}
492 
493 	public static boolean isFormSessionDocument(PojoFormBase form) {
494 		Document document = null;
495 		if (KualiDocumentFormBase.class.isAssignableFrom(form.getClass())) {
496 			KualiDocumentFormBase docForm = (KualiDocumentFormBase) form;
497 			document = docForm.getDocument();
498 		}
499 		return isDocumentSession(document, form);
500 	}
501 
502 	public static String KEY_KUALI_FORM_IN_SESSION = "KualiForm";
503 
504 	public static ActionForm getKualiForm(PageContext pageContext) {
505 		return getKualiForm((HttpServletRequest) pageContext.getRequest());
506 	}
507 
508 	public static ActionForm getKualiForm(HttpServletRequest request) {
509 		if (request.getAttribute(KEY_KUALI_FORM_IN_SESSION) != null) {
510 			return (ActionForm) request.getAttribute(KEY_KUALI_FORM_IN_SESSION);
511 		}
512 		else {
513 			final HttpSession session = request.getSession(false);
514 			return session != null ? (ActionForm) session.getAttribute(KEY_KUALI_FORM_IN_SESSION) : null;
515 		}
516 	}
517 
518 	public static boolean isPropertyEditable(Set<String> editableProperties, String propertyName) {
519 		if (LOG.isDebugEnabled()) {
520 			LOG.debug("isPropertyEditable(" + propertyName + ")");
521 		}
522 
523 		boolean returnVal = editableProperties == null
524 				|| editableProperties.contains(propertyName)
525 				|| (getIndexOfCoordinateExtension(propertyName) == -1 ? false : editableProperties
526 						.contains(propertyName.substring(0, getIndexOfCoordinateExtension(propertyName))));
527 		if (!returnVal) {
528 			if (LOG.isDebugEnabled()) {
529 				LOG.debug("isPropertyEditable(" + propertyName + ") == false / editableProperties: "
530 						+ editableProperties);
531 			}
532 		}
533 		return returnVal;
534 	}
535 
536 	public static boolean endsWithCoordinates(String parameter) {
537 		return parameter.endsWith(WebUtils.IMAGE_COORDINATE_CLICKED_X_EXTENSION)
538 				|| parameter.endsWith(WebUtils.IMAGE_COORDINATE_CLICKED_Y_EXTENSION);
539 	}
540 
541 	public static int getIndexOfCoordinateExtension(String parameter) {
542 		int indexOfCoordinateExtension = parameter.lastIndexOf(WebUtils.IMAGE_COORDINATE_CLICKED_X_EXTENSION);
543 		if (indexOfCoordinateExtension == -1)
544 			indexOfCoordinateExtension = parameter.lastIndexOf(WebUtils.IMAGE_COORDINATE_CLICKED_Y_EXTENSION);
545 		return indexOfCoordinateExtension;
546 	}
547 
548     public static boolean isInquiryHiddenField(String className, String fieldName, Object formObject, String propertyName) {
549     	boolean isHidden = false;
550     	String hiddenInquiryFields = getKualiConfigurationService().getPropertyValueAsString(className + ".hidden");
551     	if (StringUtils.isEmpty(hiddenInquiryFields)) {
552     		return isHidden;
553     	}
554     	List hiddenFields = Arrays.asList(hiddenInquiryFields.replaceAll(" ", "").split(","));
555     	if (hiddenFields.contains(fieldName.trim())) {
556     		isHidden = true;
557     	}
558     	return isHidden;
559     }
560 
561     public static boolean isHiddenKimObjectType(String type, String configParameter) {
562     	boolean hideType = false;
563     	String hiddenTypes = getKualiConfigurationService().getPropertyValueAsString(configParameter);
564     	if (StringUtils.isEmpty(hiddenTypes)) {
565     		return hideType;
566     	}
567     	List hiddenTypeValues = Arrays.asList(hiddenTypes.replaceAll(" ", "").split(","));
568     	if (hiddenTypeValues.contains(type.trim())) {
569     		hideType = true;
570     	}
571     	return hideType;
572     }
573 
574 	public static String getFullyMaskedValue(String className, String fieldName, Object formObject, String propertyName) {
575 		String displayMaskValue = null;
576 		Object propertyValue = ObjectUtils.getPropertyValue(formObject, propertyName);
577 
578 		DataDictionaryEntryBase entry = (DataDictionaryEntryBase) KRADServiceLocatorWeb.getDataDictionaryService()
579 				.getDataDictionary().getDictionaryObjectEntry(className);
580 		AttributeDefinition a = entry.getAttributeDefinition(fieldName);
581 
582 		AttributeSecurity attributeSecurity = a.getAttributeSecurity();
583 		if (attributeSecurity != null && attributeSecurity.isMask()) {
584 			MaskFormatter maskFormatter = attributeSecurity.getMaskFormatter();
585 			displayMaskValue = maskFormatter.maskValue(propertyValue);
586 
587 		}
588 		return displayMaskValue;
589 	}
590 
591 	public static String getPartiallyMaskedValue(String className, String fieldName, Object formObject,
592 			String propertyName) {
593 		String displayMaskValue = null;
594 		Object propertyValue = ObjectUtils.getPropertyValue(formObject, propertyName);
595 
596 		DataDictionaryEntryBase entry = (DataDictionaryEntryBase) KRADServiceLocatorWeb.getDataDictionaryService()
597 				.getDataDictionary().getDictionaryObjectEntry(className);
598 		AttributeDefinition a = entry.getAttributeDefinition(fieldName);
599 
600 		AttributeSecurity attributeSecurity = a.getAttributeSecurity();
601 		if (attributeSecurity != null && attributeSecurity.isPartialMask()) {
602 			MaskFormatter partialMaskFormatter = attributeSecurity.getPartialMaskFormatter();
603 			displayMaskValue = partialMaskFormatter.maskValue(propertyValue);
604 
605 		}
606 		return displayMaskValue;
607 	}
608 
609 	public static boolean canFullyUnmaskField(String businessObjectClassName, String fieldName, KualiForm form) {
610 		Class businessObjClass = null;
611 		try {
612 			businessObjClass = Class.forName(businessObjectClassName);
613 		}
614 		catch (Exception e) {
615 			throw new RuntimeException("Unable to resolve class name: " + businessObjectClassName);
616 		}
617 		if (form instanceof KualiDocumentFormBase) {
618 			return KNSServiceLocator.getBusinessObjectAuthorizationService().canFullyUnmaskField(
619 					GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName,
620 					((KualiDocumentFormBase) form).getDocument());
621 		}
622 		else {
623 			return KNSServiceLocator.getBusinessObjectAuthorizationService().canFullyUnmaskField(
624 					GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName, null);
625 		}
626 	}
627 
628 	public static boolean canPartiallyUnmaskField(String businessObjectClassName, String fieldName, KualiForm form) {
629 		Class businessObjClass = null;
630 		try {
631 			businessObjClass = Class.forName(businessObjectClassName);
632 		}
633 		catch (Exception e) {
634 			throw new RuntimeException("Unable to resolve class name: " + businessObjectClassName);
635 		}
636 		if (form instanceof KualiDocumentFormBase) {
637 			return KNSServiceLocator.getBusinessObjectAuthorizationService().canPartiallyUnmaskField(
638 					GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName,
639 					((KualiDocumentFormBase) form).getDocument());
640 		}
641 		else {
642 			return KNSServiceLocator.getBusinessObjectAuthorizationService().canPartiallyUnmaskField(
643 					GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName, null);
644 		}
645 	}
646 
647 	public static boolean canAddNoteAttachment(Document document) {
648 		boolean canViewNoteAttachment = false;
649 		DocumentAuthorizer documentAuthorizer = KNSServiceLocator.getDocumentHelperService().getDocumentAuthorizer(
650 				document);
651 		canViewNoteAttachment = documentAuthorizer.canAddNoteAttachment(document, null, GlobalVariables
652 				.getUserSession().getPerson());
653 		return canViewNoteAttachment;
654 	}
655 
656 	public static boolean canViewNoteAttachment(Document document, String attachmentTypeCode) {
657 		boolean canViewNoteAttachment = false;
658 		DocumentAuthorizer documentAuthorizer = KNSServiceLocator.getDocumentHelperService().getDocumentAuthorizer(
659 				document);
660 		canViewNoteAttachment = documentAuthorizer.canViewNoteAttachment(document, attachmentTypeCode, GlobalVariables
661 				.getUserSession().getPerson());
662 		return canViewNoteAttachment;
663 	}
664 
665 	public static boolean canDeleteNoteAttachment(Document document, String attachmentTypeCode,
666 			String authorUniversalIdentifier) {
667 		boolean canDeleteNoteAttachment = false;
668 		DocumentAuthorizer documentAuthorizer = KNSServiceLocator.getDocumentHelperService().getDocumentAuthorizer(
669 				document);
670 		canDeleteNoteAttachment = documentAuthorizer.canDeleteNoteAttachment(document, attachmentTypeCode, "false",
671 				GlobalVariables.getUserSession().getPerson());
672 		if (canDeleteNoteAttachment) {
673 			return canDeleteNoteAttachment;
674 		}
675 		else {
676 			canDeleteNoteAttachment = documentAuthorizer.canDeleteNoteAttachment(document, attachmentTypeCode, "true",
677 					GlobalVariables.getUserSession().getPerson());
678 			if (canDeleteNoteAttachment
679 					&& !authorUniversalIdentifier.equals(GlobalVariables.getUserSession().getPerson().getPrincipalId())) {
680 				canDeleteNoteAttachment = false;
681 			}
682 		}
683 		return canDeleteNoteAttachment;
684 	}
685 
686 	public static void reuseErrorMapFromPreviousRequest(KualiDocumentFormBase kualiDocumentFormBase) {
687 		if (kualiDocumentFormBase.getMessageMapFromPreviousRequest() == null) {
688 			LOG.error("Error map from previous request is null!");
689 			return;
690 		}
691 		MessageMap errorMapFromGlobalVariables = GlobalVariables.getMessageMap();
692 		if (kualiDocumentFormBase.getMessageMapFromPreviousRequest() == errorMapFromGlobalVariables) {
693 			// if we've switched them already, then return early and do nothing
694 			return;
695 		}
696 		if (!errorMapFromGlobalVariables.hasNoErrors()) {
697 			throw new RuntimeException("Cannot replace error map because it is not empty");
698 		}
699 		GlobalVariables.setMessageMap(kualiDocumentFormBase.getMessageMapFromPreviousRequest());
700 		GlobalVariables.getMessageMap().clearErrorPath();
701 	}
702 
703 	/**
704 	 * Excapes out HTML to prevent XSS attacks, and replaces the following
705 	 * strings to allow for a limited set of HTML tags
706 	 * 
707 	 * <li>[X] and [/X], where X represents any 1 or 2 letter string may be used
708 	 * to specify the equivalent tag in HTML (i.e. &lt;X&gt; and &lt;/X&gt;) <li>
709 	 * [font COLOR], where COLOR represents any valid html color (i.e. color
710 	 * name or hexcode preceeded by #) will be filtered into &lt;font
711 	 * color="COLOR"/&gt; <li>[/font] will be filtered into &lt;/font&gt; <li>
712 	 * [table CLASS], where CLASS gives the style class to use, will be filter
713 	 * into &lt;table class="CLASS"/&gt; <li>[/table] will be filtered into
714 	 * &lt;/table&gt; <li>[td CLASS], where CLASS gives the style class to use,
715 	 * will be filter into &lt;td class="CLASS"/&gt;
716 	 * 
717 	 * @param inputString
718 	 * @return
719 	 */
720 	public static String filterHtmlAndReplaceRiceMarkup(String inputString) {
721 		String outputString = StringEscapeUtils.escapeHtml(inputString);
722 		// string has been escaped of all <, >, and & (and other characters)
723 
724 		Map<String, String> findAndReplacePatterns = new HashMap<String, String>();
725 
726 		// now replace our rice custom markup into html
727 
728 		// DON'T ALLOW THE SCRIPT TAG OR ARBITRARY IMAGES/URLS/ETC. THROUGH
729 
730 		// filter any one character tags
731 		findAndReplacePatterns.put("\\[([A-Za-z])\\]", "<$1>");
732 		findAndReplacePatterns.put("\\[/([A-Za-z])\\]", "</$1>");
733 		// filter any two character tags
734 		findAndReplacePatterns.put("\\[([A-Za-z]{2})\\]", "<$1>");
735 		findAndReplacePatterns.put("\\[/([A-Za-z]{2})\\]", "</$1>");
736 		// filter the font tag
737 		findAndReplacePatterns.put("\\[font (#[0-9A-Fa-f]{1,6}|[A-Za-z]+)\\]", "<font color=\"$1\">");
738 		findAndReplacePatterns.put("\\[/font\\]", "</font>");
739 		// filter the table tag
740 		findAndReplacePatterns.put("\\[table\\]", "<table>");
741 		findAndReplacePatterns.put("\\[table ([A-Za-z]+)\\]", "<table class=\"$1\">");
742 		findAndReplacePatterns.put("\\[/table\\]", "</table>");
743 		// fiter td with class
744 		findAndReplacePatterns.put("\\[td ([A-Za-z]+)\\]", "<td class=\"$1\">");
745 
746 		for (String findPattern : findAndReplacePatterns.keySet()) {
747 			Pattern p = Pattern.compile(findPattern);
748 			Matcher m = p.matcher(outputString);
749 			if (m.find()) {
750 				String replacePattern = findAndReplacePatterns.get(findPattern);
751 				outputString = m.replaceAll(replacePattern);
752 			}
753 		}
754 
755 		return outputString;
756 	}
757 
758     /**
759 	 * Determines and returns the URL for question button images; looks first
760 	 * for a property "application.custom.image.url", and if that is missing,
761 	 * uses the image url returned by getDefaultButtonImageUrl()
762 	 * 
763 	 * @param imageName
764 	 *            the name of the image to find a button for
765 	 * @return the URL where question button images are located
766 	 */
767 	public static String getButtonImageUrl(String imageName) {
768 		String buttonImageUrl = getKualiConfigurationService().getPropertyValueAsString(
769                 WebUtils.APPLICATION_IMAGE_URL_PROPERTY_PREFIX + "." + imageName);
770 		if (StringUtils.isBlank(buttonImageUrl)) {
771 			buttonImageUrl = getDefaultButtonImageUrl(imageName);
772 		}
773 		return buttonImageUrl;
774 	}
775 
776     public static String getAttachmentImageForUrl(String contentType) {
777         String image = getKualiConfigurationService().getPropertyValueAsString(KRADConstants.ATTACHMENT_IMAGE_PREFIX + contentType);
778         if (StringUtils.isEmpty(image)) {
779             return getKualiConfigurationService().getPropertyValueAsString(KRADConstants.ATTACHMENT_IMAGE_DEFAULT);
780         }
781         return image;
782     }
783 
784 	/**
785 	 * Generates a default button image URL, in the form of:
786 	 * ${kr.externalizable.images.url}buttonsmall_${imageName}.gif
787 	 * 
788 	 * @param imageName
789 	 *            the image name to generate a default button name for
790 	 * @return the default button image url
791 	 */
792 	public static String getDefaultButtonImageUrl(String imageName) {
793 		return getKualiConfigurationService().getPropertyValueAsString(WebUtils.DEFAULT_IMAGE_URL_PROPERTY_NAME)
794 				+ "buttonsmall_" + imageName + ".gif";
795 	}
796 
797     /**
798 	 * @return an implementation of the KualiConfigurationService
799 	 */
800 	public static ConfigurationService getKualiConfigurationService() {
801 		if (configurationService == null) {
802 			configurationService = KRADServiceLocator.getKualiConfigurationService();
803 		}
804 		return configurationService;
805 	}
806 	
807     /**
808      * Takes a string an converts the whitespace which would be ignored in an
809      * HTML document into HTML elements so the whitespace is preserved
810      * 
811      * @param startingString The string to preserve whitespace in
812      * @return A string whose whitespace has been converted to HTML elements to preserve the whitespace in an HTML document
813      */
814     public static String preserveWhitespace(String startingString) {
815     	String convertedString = startingString.replaceAll("\n", "<br />");
816     	convertedString = convertedString.replaceAll("  ", "&nbsp;&nbsp;").replaceAll("(&nbsp; | &nbsp;)", "&nbsp;&nbsp;");
817     	return convertedString;
818     }
819     
820     public static String getKimGroupDisplayName(String groupId) {
821     	if(StringUtils.isBlank(groupId)) {
822     		throw new IllegalArgumentException("Group ID must have a value");
823     	}
824     	return KimApiServiceLocator.getGroupService().getGroup(groupId).getName();
825     }
826     
827     public static String getPrincipalDisplayName(String principalId) {
828     	if(StringUtils.isBlank(principalId)) {
829     		throw new IllegalArgumentException("Principal ID must have a value");
830     	}
831     	return KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId).getDefaultName().getCompositeName();
832     }
833 }