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 }