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