001/** 002 * Copyright 2005-2018 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 org.apache.commons.lang.StringEscapeUtils; 019import org.apache.commons.lang.StringUtils; 020import org.apache.log4j.Level; 021import org.apache.log4j.Logger; 022import org.apache.struts.Globals; 023import org.apache.struts.action.ActionForm; 024import org.apache.struts.action.ActionMapping; 025import org.apache.struts.action.ActionServletWrapper; 026import org.apache.struts.upload.CommonsMultipartRequestHandler; 027import org.apache.struts.upload.FormFile; 028import org.apache.struts.upload.MultipartRequestHandler; 029import org.apache.struts.upload.MultipartRequestWrapper; 030import org.kuali.rice.core.api.CoreApiServiceLocator; 031import org.kuali.rice.core.api.config.property.ConfigContext; 032import org.kuali.rice.core.api.config.property.ConfigurationService; 033import org.kuali.rice.core.api.util.RiceKeyConstants; 034import org.kuali.rice.kew.api.action.ActionRequest; 035import org.kuali.rice.kew.api.action.RecipientType; 036import org.kuali.rice.kim.api.role.Role; 037import org.kuali.rice.kim.api.services.KimApiServiceLocator; 038import org.kuali.rice.kns.datadictionary.KNSDocumentEntry; 039import org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry; 040import org.kuali.rice.kns.document.authorization.DocumentAuthorizer; 041import org.kuali.rice.kns.service.KNSServiceLocator; 042import org.kuali.rice.kns.web.struts.action.KualiMultipartRequestHandler; 043import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase; 044import org.kuali.rice.kns.web.struts.form.KualiForm; 045import org.kuali.rice.kns.web.struts.form.KualiMaintenanceForm; 046import org.kuali.rice.kns.web.struts.form.pojo.PojoFormBase; 047import org.kuali.rice.kns.web.ui.Field; 048import org.kuali.rice.kns.web.ui.Row; 049import org.kuali.rice.kns.web.ui.Section; 050import org.kuali.rice.krad.datadictionary.AttributeDefinition; 051import org.kuali.rice.krad.datadictionary.AttributeSecurity; 052import org.kuali.rice.krad.datadictionary.DataDictionary; 053import org.kuali.rice.krad.datadictionary.DataDictionaryEntryBase; 054import org.kuali.rice.krad.datadictionary.mask.MaskFormatter; 055import org.kuali.rice.krad.document.Document; 056import org.kuali.rice.krad.exception.ValidationException; 057import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 058import org.kuali.rice.krad.util.GlobalVariables; 059import org.kuali.rice.krad.util.KRADConstants; 060import org.kuali.rice.krad.util.MessageMap; 061import org.kuali.rice.krad.util.ObjectUtils; 062 063import javax.servlet.ServletException; 064import javax.servlet.http.HttpServletRequest; 065import javax.servlet.http.HttpServletResponse; 066import javax.servlet.http.HttpSession; 067import javax.servlet.jsp.PageContext; 068import java.io.BufferedInputStream; 069import java.io.ByteArrayOutputStream; 070import java.io.IOException; 071import java.io.InputStream; 072import java.io.OutputStream; 073import java.net.URI; 074import java.net.URISyntaxException; 075import java.util.Arrays; 076import java.util.Collection; 077import java.util.Enumeration; 078import java.util.HashMap; 079import java.util.Hashtable; 080import java.util.Iterator; 081import java.util.LinkedHashMap; 082import java.util.List; 083import java.util.Map; 084import java.util.Set; 085import java.util.regex.Matcher; 086import java.util.regex.Pattern; 087 088/** 089 * General helper methods for handling requests. 090 * 091 * @deprecated Only used in KNS classes, use KRAD. 092 */ 093@Deprecated 094public class WebUtils { 095 private static final Logger LOG = Logger.getLogger(WebUtils.class); 096 097 private static final String IMAGE_COORDINATE_CLICKED_X_EXTENSION = ".x"; 098 private static final String IMAGE_COORDINATE_CLICKED_Y_EXTENSION = ".y"; 099 100 private static final String APPLICATION_IMAGE_URL_PROPERTY_PREFIX = "application.custom.image.url"; 101 private static final String DEFAULT_IMAGE_URL_PROPERTY_NAME = "kr.externalizable.images.url"; 102 103 /** 104 * Prefixes indicating an absolute url 105 */ 106 private static final String[] SCHEMES = { "http://", "https://" }; 107 108 /** 109 * A request attribute name that indicates that a 110 * {@link org.kuali.rice.kns.exception.FileUploadLimitExceededException} has already been thrown for the 111 * request. 112 */ 113 public static final String FILE_UPLOAD_LIMIT_EXCEEDED_EXCEPTION_ALREADY_THROWN = "fileUploadLimitExceededExceptionAlreadyThrown"; 114 115 private static ConfigurationService configurationService; 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 = (String[]) 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 // If there are quotes in the name, we should replace them to avoid issues. 319 // The filename will be wrapped with quotes below when it is set in the header 320 String updateFileName; 321 if(fileName.contains("\"")) { 322 updateFileName = fileName.replaceAll("\"", ""); 323 } else { 324 updateFileName = fileName; 325 } 326 327 // set response 328 response.setContentType(contentType); 329 response.setHeader("Content-disposition", "attachment; filename=\"" + updateFileName + "\""); 330 response.setHeader("Expires", "0"); 331 response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0"); 332 response.setHeader("Pragma", "public"); 333 response.setContentLength(byteArrayOutputStream.size()); 334 335 // write to output 336 OutputStream outputStream = response.getOutputStream(); 337 byteArrayOutputStream.writeTo(response.getOutputStream()); 338 outputStream.flush(); 339 outputStream.close(); 340 } 341 342 /** 343 * A file that is not of type text/plain or text/html can be output through 344 * the response using this method. 345 * 346 * @param response 347 * @param contentType 348 * @param inStream 349 * @param fileName 350 */ 351 public static void saveMimeInputStreamAsFile(HttpServletResponse response, String contentType, 352 InputStream inStream, String fileName, int fileSize) throws IOException { 353 354 // If there are quotes in the name, we should replace them to avoid issues. 355 // The filename will be wrapped with quotes below when it is set in the header 356 String updateFileName; 357 if(fileName.contains("\"")) { 358 updateFileName = fileName.replaceAll("\"", ""); 359 } else { 360 updateFileName = fileName; 361 } 362 363 // set response 364 response.setContentType(contentType); 365 response.setHeader("Content-disposition", "attachment; filename=\"" + updateFileName + "\""); 366 response.setHeader("Expires", "0"); 367 response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0"); 368 response.setHeader("Pragma", "public"); 369 response.setContentLength(fileSize); 370 371 // write to output 372 OutputStream out = response.getOutputStream(); 373 BufferedInputStream is = new BufferedInputStream(inStream); 374 int data; 375 while ((data = is.read()) != -1) { 376 out.write(data); 377 } 378 is.close(); 379 out.flush(); 380 } 381 382 /** 383 * JSTL function to return the tab state of the tab from the form. 384 * 385 * @param form 386 * @param tabKey 387 * @return 388 */ 389 public static String getTabState(KualiForm form, String tabKey) { 390 return form.getTabState(tabKey); 391 } 392 393 public static void incrementTabIndex(KualiForm form, String tabKey) { 394 form.incrementTabIndex(); 395 } 396 397 /** 398 * Attempts to reopen sub tabs which would have been closed for inactive records 399 * 400 * @param sections the list of Sections whose rows and fields to set the open tab state on 401 * @param tabStates the map of tabKey->tabState. This map will be modified to set entries to "OPEN" 402 * @param collectionName the name of the collection reopening 403 */ 404 public static void reopenInactiveRecords(List<Section> sections, Map<String, String> tabStates, String collectionName) { 405 for (Section section : sections) { 406 for (Row row: section.getRows()) { 407 for (Field field : row.getFields()) { 408 if (field != null) { 409 if (Field.CONTAINER.equals(field.getFieldType()) && StringUtils.startsWith(field.getContainerName(), collectionName)) { 410 final String tabKey = WebUtils.generateTabKey(FieldUtils.generateCollectionSubTabName(field)); 411 tabStates.put(tabKey, KualiForm.TabState.OPEN.name()); 412 } 413 } 414 } 415 } 416 } 417 } 418 419 /** 420 * Generates a String from the title that can be used as a Map key. 421 * 422 * @param tabTitle 423 * @return 424 */ 425 public static String generateTabKey(String tabTitle) { 426 String key = ""; 427 if (!StringUtils.isBlank(tabTitle)) { 428 key = tabTitle.replaceAll("\\W", ""); 429 // if (key.length() > 25) { 430 // key = key.substring(0, 24); 431 // } 432 } 433 434 return key; 435 } 436 437 public static void getMultipartParameters(HttpServletRequest request, ActionServletWrapper servletWrapper, 438 ActionForm form, ActionMapping mapping) { 439 Map params = new HashMap(); 440 441 // Get the ActionServletWrapper from the form bean 442 // ActionServletWrapper servletWrapper = getServletWrapper(); 443 444 try { 445 CommonsMultipartRequestHandler multipartHandler = new CommonsMultipartRequestHandler(); 446 if (multipartHandler != null) { 447 // Set servlet and mapping info 448 if (servletWrapper != null) { 449 // from pojoformbase 450 // servlet only affects tempdir on local disk 451 servletWrapper.setServletFor(multipartHandler); 452 } 453 multipartHandler.setMapping((ActionMapping) request.getAttribute(Globals.MAPPING_KEY)); 454 // Initialize multipart request class handler 455 multipartHandler.handleRequest(request); 456 457 Collection<FormFile> files = multipartHandler.getFileElements().values(); 458 Enumeration keys = multipartHandler.getFileElements().keys(); 459 460 while (keys.hasMoreElements()) { 461 Object key = keys.nextElement(); 462 FormFile file = (FormFile) multipartHandler.getFileElements().get(key); 463 long maxSize = WebUtils.getMaxUploadSize(form); 464 if (LOG.isDebugEnabled()) { 465 LOG.debug(file.getFileSize()); 466 } 467 if (maxSize > 0 && Long.parseLong(file.getFileSize() + "") > maxSize) { 468 469 GlobalVariables.getMessageMap().putError(key.toString(), 470 RiceKeyConstants.ERROR_UPLOADFILE_SIZE, 471 new String[] { file.getFileName(), Long.toString(maxSize) }); 472 473 } 474 } 475 476 // get file elements for kualirequestprocessor 477 if (servletWrapper == null) { 478 request.setAttribute(KRADConstants.UPLOADED_FILE_REQUEST_ATTRIBUTE_KEY, 479 getFileParametersForMultipartRequest(request, multipartHandler)); 480 } 481 } 482 } 483 catch (ServletException e) { 484 throw new ValidationException("unable to handle multipart request " + e.getMessage(), e); 485 } 486 } 487 488 public static long getMaxUploadSize(ActionForm form) { 489 long max = 0L; 490 KualiMultipartRequestHandler multipartHandler = new KualiMultipartRequestHandler(); 491 if (form instanceof PojoFormBase) { 492 max = multipartHandler.calculateMaxUploadSizeToMaxOfList(((PojoFormBase) form).getMaxUploadSizes()); 493 } 494 if (LOG.isDebugEnabled()) { 495 LOG.debug("Max File Upload Size: " + max); 496 } 497 return max; 498 } 499 500 private static Map getFileParametersForMultipartRequest(HttpServletRequest request, 501 MultipartRequestHandler multipartHandler) { 502 Map parameters = new HashMap(); 503 Hashtable elements = multipartHandler.getFileElements(); 504 Enumeration e = elements.keys(); 505 while (e.hasMoreElements()) { 506 String key = (String) e.nextElement(); 507 parameters.put(key, elements.get(key)); 508 } 509 510 if (request instanceof MultipartRequestWrapper) { 511 request = (HttpServletRequest) ((MultipartRequestWrapper) request).getRequest(); 512 e = request.getParameterNames(); 513 while (e.hasMoreElements()) { 514 String key = (String) e.nextElement(); 515 parameters.put(key, request.getParameterValues(key)); 516 } 517 } 518 else { 519 LOG.debug("Gathering multipart parameters for unwrapped request"); 520 } 521 return parameters; 522 } 523 524 // end multipart 525 526 public static void registerEditableProperty(PojoFormBase form, String editablePropertyName) { 527 form.registerEditableProperty(editablePropertyName); 528 } 529 530 public static boolean isDocumentSession(Document document, PojoFormBase docForm) { 531 boolean sessionDoc = document instanceof org.kuali.rice.krad.document.SessionDocument; 532 boolean dataDictionarySessionDoc = false; 533 if (!sessionDoc) { 534 DataDictionary dataDictionary = KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary(); 535 if (docForm instanceof KualiMaintenanceForm) { 536 KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) docForm; 537 if (dataDictionary != null) { 538 if (maintenanceForm.getDocTypeName() != null) { 539 MaintenanceDocumentEntry maintenanceDocumentEntry = (MaintenanceDocumentEntry) dataDictionary.getDocumentEntry(maintenanceForm.getDocTypeName()); 540 dataDictionarySessionDoc = maintenanceDocumentEntry.isSessionDocument(); 541 } 542 } 543 } 544 else { 545 if (document != null && dataDictionary != null) { 546 KNSDocumentEntry documentEntry = (KNSDocumentEntry) dataDictionary.getDocumentEntry(document.getClass().getName()); 547 dataDictionarySessionDoc = documentEntry.isSessionDocument(); 548 } 549 } 550 } 551 return sessionDoc || dataDictionarySessionDoc; 552 } 553 554 public static boolean isFormSessionDocument(PojoFormBase form) { 555 Document document = null; 556 if (KualiDocumentFormBase.class.isAssignableFrom(form.getClass())) { 557 KualiDocumentFormBase docForm = (KualiDocumentFormBase) form; 558 document = docForm.getDocument(); 559 } 560 return isDocumentSession(document, form); 561 } 562 563 public static String KEY_KUALI_FORM_IN_SESSION = "KualiForm"; 564 565 public static ActionForm getKualiForm(PageContext pageContext) { 566 return getKualiForm((HttpServletRequest) pageContext.getRequest()); 567 } 568 569 public static ActionForm getKualiForm(HttpServletRequest request) { 570 if (request.getAttribute(KEY_KUALI_FORM_IN_SESSION) != null) { 571 return (ActionForm) request.getAttribute(KEY_KUALI_FORM_IN_SESSION); 572 } 573 else { 574 final HttpSession session = request.getSession(false); 575 return session != null ? (ActionForm) session.getAttribute(KEY_KUALI_FORM_IN_SESSION) : null; 576 } 577 } 578 579 public static boolean isPropertyEditable(Set<String> editableProperties, String propertyName) { 580 if (LOG.isDebugEnabled()) { 581 LOG.debug("isPropertyEditable(" + propertyName + ")"); 582 } 583 584 boolean returnVal = editableProperties == null 585 || editableProperties.contains(propertyName) 586 || (getIndexOfCoordinateExtension(propertyName) == -1 ? false : editableProperties 587 .contains(propertyName.substring(0, getIndexOfCoordinateExtension(propertyName)))); 588 if (!returnVal) { 589 if (LOG.isDebugEnabled()) { 590 LOG.debug("isPropertyEditable(" + propertyName + ") == false / editableProperties: " 591 + editableProperties); 592 } 593 } 594 return returnVal; 595 } 596 597 public static boolean endsWithCoordinates(String parameter) { 598 return parameter.endsWith(WebUtils.IMAGE_COORDINATE_CLICKED_X_EXTENSION) 599 || parameter.endsWith(WebUtils.IMAGE_COORDINATE_CLICKED_Y_EXTENSION); 600 } 601 602 public static int getIndexOfCoordinateExtension(String parameter) { 603 int indexOfCoordinateExtension = parameter.lastIndexOf(WebUtils.IMAGE_COORDINATE_CLICKED_X_EXTENSION); 604 if (indexOfCoordinateExtension == -1) { 605 indexOfCoordinateExtension = parameter.lastIndexOf(WebUtils.IMAGE_COORDINATE_CLICKED_Y_EXTENSION); 606 } 607 return indexOfCoordinateExtension; 608 } 609 610 public static boolean isInquiryHiddenField(String className, String fieldName, Object formObject, String propertyName) { 611 boolean isHidden = false; 612 String hiddenInquiryFields = getKualiConfigurationService().getPropertyValueAsString(className + ".hidden"); 613 if (StringUtils.isEmpty(hiddenInquiryFields)) { 614 return isHidden; 615 } 616 List hiddenFields = Arrays.asList(hiddenInquiryFields.replaceAll(" ", "").split(",")); 617 if (hiddenFields.contains(fieldName.trim())) { 618 isHidden = true; 619 } 620 return isHidden; 621 } 622 623 public static boolean isHiddenKimObjectType(String type, String configParameter) { 624 boolean hideType = false; 625 String hiddenTypes = getKualiConfigurationService().getPropertyValueAsString(configParameter); 626 if (StringUtils.isEmpty(hiddenTypes)) { 627 return hideType; 628 } 629 List hiddenTypeValues = Arrays.asList(hiddenTypes.replaceAll(" ", "").split(",")); 630 if (hiddenTypeValues.contains(type.trim())) { 631 hideType = true; 632 } 633 return hideType; 634 } 635 636 public static String getFullyMaskedValue(String className, String fieldName, Object formObject, String propertyName) { 637 String displayMaskValue = null; 638 Object propertyValue = ObjectUtils.getPropertyValue(formObject, propertyName); 639 640 DataDictionaryEntryBase entry = (DataDictionaryEntryBase) KRADServiceLocatorWeb.getDataDictionaryService() 641 .getDataDictionary().getDictionaryObjectEntry(className); 642 AttributeDefinition a = entry.getAttributeDefinition(fieldName); 643 644 AttributeSecurity attributeSecurity = a.getAttributeSecurity(); 645 if (attributeSecurity != null && attributeSecurity.isMask()) { 646 MaskFormatter maskFormatter = attributeSecurity.getMaskFormatter(); 647 displayMaskValue = maskFormatter.maskValue(propertyValue); 648 649 } 650 return displayMaskValue; 651 } 652 653 public static String getPartiallyMaskedValue(String className, String fieldName, Object formObject, 654 String propertyName) { 655 String displayMaskValue = null; 656 Object propertyValue = ObjectUtils.getPropertyValue(formObject, propertyName); 657 658 DataDictionaryEntryBase entry = (DataDictionaryEntryBase) KRADServiceLocatorWeb.getDataDictionaryService() 659 .getDataDictionary().getDictionaryObjectEntry(className); 660 AttributeDefinition a = entry.getAttributeDefinition(fieldName); 661 662 AttributeSecurity attributeSecurity = a.getAttributeSecurity(); 663 if (attributeSecurity != null && attributeSecurity.isPartialMask()) { 664 MaskFormatter partialMaskFormatter = attributeSecurity.getPartialMaskFormatter(); 665 displayMaskValue = partialMaskFormatter.maskValue(propertyValue); 666 667 } 668 return displayMaskValue; 669 } 670 671 public static boolean canFullyUnmaskField(String businessObjectClassName, String fieldName, KualiForm form) { 672 Class businessObjClass = null; 673 try { 674 businessObjClass = Class.forName(businessObjectClassName); 675 } 676 catch (Exception e) { 677 throw new RuntimeException("Unable to resolve class name: " + businessObjectClassName); 678 } 679 if (form instanceof KualiDocumentFormBase) { 680 return KNSServiceLocator.getBusinessObjectAuthorizationService().canFullyUnmaskField( 681 GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName, 682 ((KualiDocumentFormBase) form).getDocument()); 683 } 684 else { 685 return KNSServiceLocator.getBusinessObjectAuthorizationService().canFullyUnmaskField( 686 GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName, null); 687 } 688 } 689 690 public static boolean canPartiallyUnmaskField(String businessObjectClassName, String fieldName, KualiForm form) { 691 Class businessObjClass = null; 692 try { 693 businessObjClass = Class.forName(businessObjectClassName); 694 } 695 catch (Exception e) { 696 throw new RuntimeException("Unable to resolve class name: " + businessObjectClassName); 697 } 698 if (form instanceof KualiDocumentFormBase) { 699 return KNSServiceLocator.getBusinessObjectAuthorizationService().canPartiallyUnmaskField( 700 GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName, 701 ((KualiDocumentFormBase) form).getDocument()); 702 } 703 else { 704 return KNSServiceLocator.getBusinessObjectAuthorizationService().canPartiallyUnmaskField( 705 GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName, null); 706 } 707 } 708 709 public static boolean canAddNoteAttachment(Document document) { 710 boolean canViewNoteAttachment = false; 711 DocumentAuthorizer documentAuthorizer = KNSServiceLocator.getDocumentHelperService().getDocumentAuthorizer( 712 document); 713 canViewNoteAttachment = documentAuthorizer.canAddNoteAttachment(document, null, GlobalVariables 714 .getUserSession().getPerson()); 715 return canViewNoteAttachment; 716 } 717 718 public static boolean canViewNoteAttachment(Document document, String attachmentTypeCode) { 719 boolean canViewNoteAttachment = false; 720 DocumentAuthorizer documentAuthorizer = KNSServiceLocator.getDocumentHelperService().getDocumentAuthorizer( 721 document); 722 canViewNoteAttachment = documentAuthorizer.canViewNoteAttachment(document, attachmentTypeCode, GlobalVariables 723 .getUserSession().getPerson()); 724 return canViewNoteAttachment; 725 } 726 727 public static boolean canDeleteNoteAttachment(Document document, String attachmentTypeCode, 728 String authorUniversalIdentifier) { 729 boolean canDeleteNoteAttachment = false; 730 DocumentAuthorizer documentAuthorizer = KNSServiceLocator.getDocumentHelperService().getDocumentAuthorizer( 731 document); 732 canDeleteNoteAttachment = documentAuthorizer.canDeleteNoteAttachment(document, attachmentTypeCode, "false", 733 GlobalVariables.getUserSession().getPerson()); 734 if (canDeleteNoteAttachment) { 735 return canDeleteNoteAttachment; 736 } 737 else { 738 canDeleteNoteAttachment = documentAuthorizer.canDeleteNoteAttachment(document, attachmentTypeCode, "true", 739 GlobalVariables.getUserSession().getPerson()); 740 if (canDeleteNoteAttachment 741 && !authorUniversalIdentifier.equals(GlobalVariables.getUserSession().getPerson().getPrincipalId())) { 742 canDeleteNoteAttachment = false; 743 } 744 } 745 return canDeleteNoteAttachment; 746 } 747 748 public static void reuseErrorMapFromPreviousRequest(KualiDocumentFormBase kualiDocumentFormBase) { 749 if (kualiDocumentFormBase.getMessageMapFromPreviousRequest() == null) { 750 LOG.error("Error map from previous request is null!"); 751 return; 752 } 753 MessageMap errorMapFromGlobalVariables = GlobalVariables.getMessageMap(); 754 if (kualiDocumentFormBase.getMessageMapFromPreviousRequest() == errorMapFromGlobalVariables) { 755 // if we've switched them already, then return early and do nothing 756 return; 757 } 758 if (!errorMapFromGlobalVariables.hasNoErrors()) { 759 throw new RuntimeException("Cannot replace error map because it is not empty"); 760 } 761 GlobalVariables.setMessageMap(kualiDocumentFormBase.getMessageMapFromPreviousRequest()); 762 GlobalVariables.getMessageMap().clearErrorPath(); 763 } 764 765 /** 766 * Excapes out HTML to prevent XSS attacks, and replaces the following 767 * strings to allow for a limited set of HTML tags 768 * 769 * <li>[X] and [/X], where X represents any 1 or 2 letter string may be used 770 * to specify the equivalent tag in HTML (i.e. <X> and </X>) <li> 771 * [font COLOR], where COLOR represents any valid html color (i.e. color 772 * name or hexcode preceeded by #) will be filtered into <font 773 * color="COLOR"/> <li>[/font] will be filtered into </font> <li> 774 * [table CLASS], where CLASS gives the style class to use, will be filter 775 * into <table class="CLASS"/> <li>[/table] will be filtered into 776 * </table> <li>[td CLASS], where CLASS gives the style class to use, 777 * will be filter into <td class="CLASS"/> 778 * 779 * @param inputString 780 * @return 781 */ 782 public static String filterHtmlAndReplaceRiceMarkup(String inputString) { 783 String outputString = StringEscapeUtils.escapeHtml(inputString); 784 // string has been escaped of all <, >, and & (and other characters) 785 786 Map<String, String> findAndReplacePatterns = new LinkedHashMap<String, String>(); 787 788 // now replace our rice custom markup into html 789 790 // DON'T ALLOW THE SCRIPT TAG OR ARBITRARY IMAGES/URLS/ETC. THROUGH 791 792 //strip out instances where javascript precedes a URL 793 findAndReplacePatterns.put("\\[a ((javascript|JAVASCRIPT|JavaScript).+)\\]", ""); 794 //turn passed a href value into appropriate tag 795 findAndReplacePatterns.put("\\[a (.+)\\]", "<a href=\"$1\">"); 796 findAndReplacePatterns.put("\\[/a\\]", "</a>"); 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 = CoreApiServiceLocator.getKualiConfigurationService(); 871 } 872 return configurationService; 873 } 874 875 /** 876 * Takes a string an converts the whitespace which would be ignored in an 877 * HTML document into HTML elements so the whitespace is preserved 878 * 879 * @param startingString The string to preserve whitespace in 880 * @return A string whose whitespace has been converted to HTML elements to preserve the whitespace in an HTML document 881 */ 882 public static String preserveWhitespace(String startingString) { 883 String convertedString = startingString.replaceAll("\n", "<br />"); 884 convertedString = convertedString.replaceAll(" ", " ").replaceAll("( | )", " "); 885 return convertedString; 886 } 887 888 public static String getKimGroupDisplayName(String groupId) { 889 if(StringUtils.isBlank(groupId)) { 890 throw new IllegalArgumentException("Group ID must have a value"); 891 } 892 return KimApiServiceLocator.getGroupService().getGroup(groupId).getName(); 893 } 894 895 public static String getPrincipalDisplayName(String principalId) { 896 if(StringUtils.isBlank(principalId)) { 897 throw new IllegalArgumentException("Principal ID must have a value"); 898 } 899 if (KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId) == null){ 900 return ""; 901 } 902 else { 903 return KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId).getDefaultName().getCompositeName(); 904 } 905 } 906 907 /** 908 * Takes an {@link org.kuali.rice.kew.api.action.ActionRequest} with a recipient type of 909 * {@link org.kuali.rice.kew.api.action.RecipientType#ROLE} and returns the display name for the role. 910 * 911 * @param actionRequest the action request 912 * @return the display name for the role 913 * @throws IllegalArgumentException if the action request is null, or the recipient type is not ROLE 914 */ 915 public static String getRoleDisplayName(ActionRequest actionRequest) { 916 String result; 917 918 if(actionRequest == null) { 919 throw new IllegalArgumentException("actionRequest must be non-null"); 920 } 921 if (RecipientType.ROLE != actionRequest.getRecipientType()) { 922 throw new IllegalArgumentException("actionRequest recipient must be a Role"); 923 } 924 925 Role role = KimApiServiceLocator.getRoleService().getRole(actionRequest.getRoleName()); 926 927 if (role != null) { 928 result = role.getName(); 929 } else if (!StringUtils.isBlank(actionRequest.getQualifiedRoleNameLabel())) { 930 result = actionRequest.getQualifiedRoleNameLabel(); 931 } else { 932 result = actionRequest.getRoleName(); 933 } 934 935 return result; 936 } 937 938 /** 939 * Returns an absolute URL which is a combination of a base part plus path, 940 * or in the case that the path is already an absolute URL, the path alone 941 * @param base the url base path 942 * @param path the path to append to base 943 * @return an absolute URL representing the combination of base+path, or path alone if it is already absolute 944 */ 945 public static String toAbsoluteURL(String base, String path) { 946 boolean abs = false; 947 if (StringUtils.isBlank(path)) { 948 path = ""; 949 } else { 950 for (String scheme: SCHEMES) { 951 if (path.startsWith(scheme)) { 952 abs = true; 953 break; 954 } 955 } 956 } 957 if (abs) { 958 return path; 959 } 960 return base + path; 961 } 962 963 public static String sanitizeBackLocation(String backLocation) { 964 if(StringUtils.isBlank(backLocation)) { 965 return backLocation; 966 } 967 // if it's a relative URL then we should be good regardless 968 try { 969 URI uri = new URI(backLocation); 970 if (!uri.isAbsolute()) { 971 return backLocation; 972 } 973 } catch (URISyntaxException e) { 974 // move on to the other checks if it doesn't parse up as a URI for some reason 975 } 976 Pattern pattern = Pattern.compile(ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.BACK_LOCATION_ALLOWED_REGEX)); 977 if(pattern.matcher(backLocation).matches()) { 978 return backLocation; 979 } else { 980 return ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.BACK_LOCATION_DEFAULT_URL); 981 } 982 } 983 984}