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