View Javadoc

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