001 /**
002 * Copyright 2005-2012 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.kns.util;
017
018 import org.apache.commons.lang.StringEscapeUtils;
019 import org.apache.commons.lang.StringUtils;
020 import org.apache.log4j.Level;
021 import org.apache.log4j.Logger;
022 import org.apache.struts.Globals;
023 import org.apache.struts.action.ActionForm;
024 import org.apache.struts.action.ActionMapping;
025 import org.apache.struts.action.ActionServletWrapper;
026 import org.apache.struts.upload.CommonsMultipartRequestHandler;
027 import org.apache.struts.upload.FormFile;
028 import org.apache.struts.upload.MultipartRequestHandler;
029 import org.apache.struts.upload.MultipartRequestWrapper;
030 import org.kuali.rice.core.api.config.property.ConfigurationService;
031 import org.kuali.rice.core.api.util.RiceKeyConstants;
032 import org.kuali.rice.kns.datadictionary.KNSDocumentEntry;
033 import org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry;
034 import org.kuali.rice.kns.document.authorization.DocumentAuthorizer;
035 import org.kuali.rice.kns.service.KNSServiceLocator;
036 import org.kuali.rice.kns.web.struts.action.KualiMultipartRequestHandler;
037 import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
038 import org.kuali.rice.kns.web.struts.form.KualiForm;
039 import org.kuali.rice.kns.web.struts.form.KualiMaintenanceForm;
040 import org.kuali.rice.kns.web.struts.form.pojo.PojoFormBase;
041 import org.kuali.rice.krad.datadictionary.AttributeDefinition;
042 import org.kuali.rice.krad.datadictionary.AttributeSecurity;
043 import org.kuali.rice.krad.datadictionary.DataDictionary;
044 import org.kuali.rice.krad.datadictionary.DataDictionaryEntryBase;
045 import org.kuali.rice.krad.datadictionary.mask.MaskFormatter;
046 import org.kuali.rice.krad.document.Document;
047 import org.kuali.rice.krad.exception.ValidationException;
048 import org.kuali.rice.krad.service.KRADServiceLocator;
049 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
050 import org.kuali.rice.krad.util.GlobalVariables;
051 import org.kuali.rice.krad.util.KRADConstants;
052 import org.kuali.rice.krad.util.MessageMap;
053 import org.kuali.rice.krad.util.ObjectUtils;
054
055 import javax.servlet.ServletException;
056 import javax.servlet.http.HttpServletRequest;
057 import javax.servlet.http.HttpServletResponse;
058 import javax.servlet.http.HttpSession;
059 import javax.servlet.jsp.PageContext;
060 import java.io.ByteArrayOutputStream;
061 import java.io.IOException;
062 import java.io.InputStream;
063 import java.io.OutputStream;
064 import java.util.Arrays;
065 import java.util.Collection;
066 import java.util.Enumeration;
067 import java.util.HashMap;
068 import java.util.Hashtable;
069 import java.util.Iterator;
070 import java.util.List;
071 import java.util.Map;
072 import java.util.Set;
073 import java.util.regex.Matcher;
074 import java.util.regex.Pattern;
075
076 /**
077 * General helper methods for handling requests.
078 */
079 public class WebUtils {
080 private static final Logger LOG = Logger.getLogger(WebUtils.class);
081
082 private static final String IMAGE_COORDINATE_CLICKED_X_EXTENSION = ".x";
083 private static final String IMAGE_COORDINATE_CLICKED_Y_EXTENSION = ".y";
084
085 private static final String APPLICATION_IMAGE_URL_PROPERTY_PREFIX = "application.custom.image.url";
086 private static final String DEFAULT_IMAGE_URL_PROPERTY_NAME = "kr.externalizable.images.url";
087
088 /**
089 * A request attribute name that indicates that a
090 * {@link org.kuali.rice.kns.exception.FileUploadLimitExceededException} has already been thrown for the
091 * request.
092 */
093 public static final String FILE_UPLOAD_LIMIT_EXCEEDED_EXCEPTION_ALREADY_THROWN = "fileUploadLimitExceededExceptionAlreadyThrown";
094
095 private static ConfigurationService configurationService;
096
097 /**
098 * Checks for methodToCall parameter, and picks off the value using set dot
099 * 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 = KNSServiceLocator.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 = KNSServiceLocator.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 = KNSServiceLocator.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. <X> and </X>) <li>
707 * [font COLOR], where COLOR represents any valid html color (i.e. color
708 * name or hexcode preceeded by #) will be filtered into <font
709 * color="COLOR"/> <li>[/font] will be filtered into </font> <li>
710 * [table CLASS], where CLASS gives the style class to use, will be filter
711 * into <table class="CLASS"/> <li>[/table] will be filtered into
712 * </table> <li>[td CLASS], where CLASS gives the style class to use,
713 * will be filter into <td class="CLASS"/>
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
797 /**
798 * Takes a string an converts the whitespace which would be ignored in an
799 * HTML document into HTML elements so the whitespace is preserved
800 *
801 * @param startingString The string to preserve whitespace in
802 * @return A string whose whitespace has been converted to HTML elements to preserve the whitespace in an HTML document
803 */
804 public static String preserveWhitespace(String startingString) {
805 String convertedString = startingString.replaceAll("\n", "<br />");
806 convertedString = convertedString.replaceAll(" ", " ").replaceAll("( | )", " ");
807 return convertedString;
808 }
809 }