Java tutorial
/** * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2018 Kuali, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.kns.util; import edu.cornell.cynergy.kns.util.CynergyWebUtils; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.net.URLCodec; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.struts.Globals; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionServletWrapper; import org.apache.struts.upload.CommonsMultipartRequestHandler; import org.apache.struts.upload.FormFile; import org.apache.struts.upload.MultipartRequestHandler; import org.apache.struts.upload.MultipartRequestWrapper; import org.kuali.kfs.kns.datadictionary.KNSDocumentEntry; import org.kuali.kfs.kns.datadictionary.MaintenanceDocumentEntry; import org.kuali.kfs.kns.document.authorization.DocumentAuthorizer; import org.kuali.kfs.kns.exception.FileUploadLimitExceededException; import org.kuali.kfs.kns.service.KNSServiceLocator; import org.kuali.kfs.kns.web.struts.action.KualiMultipartRequestHandler; import org.kuali.kfs.kns.web.struts.form.KualiDocumentFormBase; import org.kuali.kfs.kns.web.struts.form.KualiForm; import org.kuali.kfs.kns.web.struts.form.KualiMaintenanceForm; import org.kuali.kfs.kns.web.struts.form.pojo.PojoFormBase; import org.kuali.kfs.kns.web.ui.Field; import org.kuali.kfs.kns.web.ui.Row; import org.kuali.kfs.kns.web.ui.Section; import org.kuali.kfs.krad.datadictionary.AttributeDefinition; import org.kuali.kfs.krad.datadictionary.AttributeSecurity; import org.kuali.kfs.krad.datadictionary.DataDictionary; import org.kuali.kfs.krad.datadictionary.DataDictionaryEntryBase; import org.kuali.kfs.krad.datadictionary.mask.MaskFormatter; import org.kuali.kfs.krad.document.Document; import org.kuali.kfs.krad.document.SessionDocument; import org.kuali.kfs.krad.exception.ValidationException; import org.kuali.kfs.krad.service.KRADServiceLocator; import org.kuali.kfs.krad.service.KRADServiceLocatorWeb; import org.kuali.kfs.krad.util.GlobalVariables; import org.kuali.kfs.krad.util.KRADConstants; import org.kuali.kfs.krad.util.MessageMap; import org.kuali.kfs.krad.util.ObjectUtils; import org.kuali.rice.core.api.config.property.ConfigContext; import org.kuali.rice.core.api.config.property.ConfigurationService; import org.kuali.rice.core.api.util.RiceKeyConstants; import org.kuali.rice.kew.api.action.ActionRequest; import org.kuali.rice.kew.api.action.RecipientType; import org.kuali.rice.kim.api.role.Role; import org.kuali.rice.kim.api.services.KimApiServiceLocator; import org.springframework.web.util.HtmlUtils; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.servlet.jsp.PageContext; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * General helper methods for handling requests. */ public class WebUtils { private static final Logger LOG = LogManager.getLogger(WebUtils.class); private static final String IMAGE_COORDINATE_CLICKED_X_EXTENSION = ".x"; private static final String IMAGE_COORDINATE_CLICKED_Y_EXTENSION = ".y"; private static final String APPLICATION_IMAGE_URL_PROPERTY_PREFIX = "application.custom.image.url"; private static final String DEFAULT_IMAGE_URL_PROPERTY_NAME = "kr.externalizable.images.url"; /** * Prefixes indicating an absolute url */ private static final String[] SCHEMES = { "http://", "https://" }; /** * A request attribute name that indicates that a * {@link FileUploadLimitExceededException} has already been thrown for the * request. */ public static final String FILE_UPLOAD_LIMIT_EXCEEDED_EXCEPTION_ALREADY_THROWN = "fileUploadLimitExceededExceptionAlreadyThrown"; public static String KEY_KUALI_FORM_IN_SESSION = "KualiForm"; private static ConfigurationService configurationService; private static URLCodec urlCodec = new URLCodec("UTF-8"); /** * Checks for methodToCall parameter, and picks off the value using set dot * notation. Handles the problem of image submits. * * @param request * @return methodToCall String */ public static String parseMethodToCall(ActionForm form, HttpServletRequest request) { String methodToCall = null; // check if is specified cleanly if (StringUtils.isNotBlank(request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER))) { if (form instanceof KualiForm && !((KualiForm) form).shouldMethodToCallParameterBeUsed( KRADConstants.DISPATCH_REQUEST_PARAMETER, request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER), request)) { throw new RuntimeException("Cannot verify that the methodToCall should be " + request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER)); } methodToCall = request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER); // include .x at the end of the parameter to make it consistent w/ // other parameters request.setAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE, KRADConstants.DISPATCH_REQUEST_PARAMETER + "." + methodToCall + IMAGE_COORDINATE_CLICKED_X_EXTENSION); } /** * The reason why we are checking for a ".x" at the end of the parameter * name: It is for the image names that in addition to sending the form * data, the web browser sends the x,y coordinate of where the user * clicked on the image. If the image input is not given a name then the * browser sends the x and y coordinates as the "x" and "y" input * fields. If the input image does have a name, the x and y coordinates * are sent using the format name.x and name.y. */ if (methodToCall == null) { // iterate through parameters looking for methodToCall for (Enumeration i = request.getParameterNames(); i.hasMoreElements();) { String parameterName = (String) i.nextElement(); // check if the parameter name is a specifying the methodToCall if (isMethodToCall(parameterName)) { methodToCall = getMethodToCallSettingAttribute(form, request, parameterName); break; } else { // KULRICE-1218: Check if the parameter's values match (not // just the name) for (String value : request.getParameterValues(parameterName)) { // adding period to startsWith check - don't want to get // confused with methodToCallFoobar if (isMethodToCall(value)) { methodToCall = getMethodToCallSettingAttribute(form, request, value); // why is there not a break outer loop here? } } } } } return methodToCall; } /** * Checks if a string signifies a methodToCall string * * @param string the string to check * @return true if is a methodToCall */ private static boolean isMethodToCall(String string) { // adding period to startsWith check - don't want to get confused with // methodToCallFoobar return string.startsWith(KRADConstants.DISPATCH_REQUEST_PARAMETER + "."); } /** * Parses out the methodToCall command and also sets the request attribute * for the methodToCall. * * @param form the ActionForm * @param request the request to set the attribute on * @param string the methodToCall string * @return the methodToCall command */ private static String getMethodToCallSettingAttribute(ActionForm form, HttpServletRequest request, String string) { if (form instanceof KualiForm && !((KualiForm) form).shouldMethodToCallParameterBeUsed(string, request.getParameter(string), request)) { throw new RuntimeException("Cannot verify that the methodToCall should be " + string); } // always adding a coordinate even if not an image final String attributeValue = endsWithCoordinates(string) ? string : string + IMAGE_COORDINATE_CLICKED_X_EXTENSION; final String methodToCall = StringUtils.substringBetween(attributeValue, KRADConstants.DISPATCH_REQUEST_PARAMETER + ".", "."); request.setAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE, attributeValue); return methodToCall; } /** * Iterates through and logs (at the given level) all attributes and * parameters of the given request onto the given Logger * * @param request * @param logger */ public static void logRequestContents(Logger logger, Level level, HttpServletRequest request) { if (logger.isEnabled(level)) { logger.log(level, "--------------------"); logger.log(level, "HttpRequest attributes:"); for (Enumeration e = request.getAttributeNames(); e.hasMoreElements();) { String attrName = (String) e.nextElement(); Object attrValue = request.getAttribute(attrName); if (attrValue.getClass().isArray()) { logCollection(logger, level, attrName, Arrays.asList((Object[]) attrValue)); } else if (attrValue instanceof Collection) { logCollection(logger, level, attrName, (Collection) attrValue); } else if (attrValue instanceof Map) { logMap(logger, level, attrName, (Map) attrValue); } else { logObject(logger, level, attrName, attrValue); } } logger.log(level, "--------------------"); logger.log(level, "HttpRequest parameters:"); for (Enumeration i = request.getParameterNames(); i.hasMoreElements();) { String paramName = (String) i.nextElement(); String[] paramValues = (String[]) request.getParameterValues(paramName); logArray(logger, level, paramName, paramValues); } logger.log(level, "--------------------"); } } private static void logArray(Logger logger, Level level, String arrayName, Object[] array) { StringBuffer value = new StringBuffer("["); for (int i = 0; i < array.length; ++i) { if (i > 0) { value.append(","); } value.append(array[i]); } value.append("]"); logThing(logger, level, arrayName, value); } private static void logCollection(Logger logger, Level level, String collectionName, Collection c) { StringBuffer value = new StringBuffer("{"); for (Iterator i = c.iterator(); i.hasNext();) { value.append(i.next()); if (i.hasNext()) { value.append(","); } } value.append("}"); logThing(logger, level, collectionName, value); } private static void logMap(Logger logger, Level level, String mapName, Map m) { StringBuffer value = new StringBuffer("{"); for (Iterator i = m.entrySet().iterator(); i.hasNext();) { Map.Entry e = (Map.Entry) i.next(); value.append("('" + e.getKey() + "','" + e.getValue() + "')"); } value.append("}"); logThing(logger, level, mapName, value); } private static void logObject(Logger logger, Level level, String objectName, Object o) { logThing(logger, level, objectName, "'" + o + "'"); } private static void logThing(Logger logger, Level level, String thingName, Object thing) { logger.log(level, " '" + thingName + "' => " + thing); } /** * A file that is not of type text/plain or text/html can be output through * the response using this method. * * @param response * @param contentType * @param byteArrayOutputStream * @param fileName */ public static void saveMimeOutputStreamAsFile(HttpServletResponse response, String contentType, ByteArrayOutputStream byteArrayOutputStream, String fileName) throws IOException { // If there are quotes in the name, we should replace them to avoid issues. // The filename will be wrapped with quotes below when it is set in the header String updateFileName; if (fileName.contains("\"")) { updateFileName = fileName.replaceAll("\"", ""); } else { updateFileName = fileName; } // set response response.setContentType(contentType); response.setHeader("Content-disposition", "attachment; filename=\"" + updateFileName + "\""); response.setHeader("Expires", "0"); response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0"); response.setHeader("Pragma", "public"); response.setContentLength(byteArrayOutputStream.size()); // write to output OutputStream outputStream = response.getOutputStream(); byteArrayOutputStream.writeTo(response.getOutputStream()); outputStream.flush(); outputStream.close(); } /** * A file that is not of type text/plain or text/html can be output through * the response using this method. * * @param response * @param contentType * @param inStream * @param fileName */ public static void saveMimeInputStreamAsFile(HttpServletResponse response, String contentType, InputStream inStream, String fileName, int fileSize) throws IOException { // If there are quotes in the name, we should replace them to avoid issues. // The filename will be wrapped with quotes below when it is set in the header String updateFileName; if (fileName.contains("\"")) { updateFileName = fileName.replaceAll("\"", ""); } else { updateFileName = fileName; } // set response response.setContentType(contentType); response.setHeader("Content-disposition", "attachment; filename=\"" + updateFileName + "\""); response.setHeader("Expires", "0"); response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0"); response.setHeader("Pragma", "public"); response.setContentLength(fileSize); // ==== CU Customization: Write the data via buffering for efficiency. ==== CynergyWebUtils.performBufferedWriteToStream(inStream, response.getOutputStream()); } /** * JSTL function to return the tab state of the tab from the form. * * @param form * @param tabKey * @return */ public static String getTabState(KualiForm form, String tabKey) { return form.getTabState(tabKey); } public static void incrementTabIndex(KualiForm form, String tabKey) { form.incrementTabIndex(); } /** * Attempts to reopen sub tabs which would have been closed for inactive records * * @param sections the list of Sections whose rows and fields to set the open tab state on * @param tabStates the map of tabKey->tabState. This map will be modified to set entries to "OPEN" * @param collectionName the name of the collection reopening */ public static void reopenInactiveRecords(List<Section> sections, Map<String, String> tabStates, String collectionName) { for (Section section : sections) { for (Row row : section.getRows()) { for (Field field : row.getFields()) { if (field != null) { if (Field.CONTAINER.equals(field.getFieldType()) && StringUtils.startsWith(field.getContainerName(), collectionName)) { final String tabKey = WebUtils .generateTabKey(FieldUtils.generateCollectionSubTabName(field)); tabStates.put(tabKey, KualiForm.TabState.OPEN.name()); } } } } } } /** * Generates a String from the title that can be used as a Map key. * * @param tabTitle * @return */ public static String generateTabKey(String tabTitle) { String key = ""; if (!StringUtils.isBlank(tabTitle)) { key = tabTitle.replaceAll("\\W", ""); } return key; } public static void getMultipartParameters(HttpServletRequest request, ActionServletWrapper servletWrapper, ActionForm form, ActionMapping mapping) { // Get the ActionServletWrapper from the form bean // ActionServletWrapper servletWrapper = getServletWrapper(); try { CommonsMultipartRequestHandler multipartHandler = new CommonsMultipartRequestHandler(); if (multipartHandler != null) { // Set servlet and mapping info if (servletWrapper != null) { // from pojoformbase // servlet only affects tempdir on local disk servletWrapper.setServletFor(multipartHandler); } multipartHandler.setMapping((ActionMapping) request.getAttribute(Globals.MAPPING_KEY)); // Initialize multipart request class handler multipartHandler.handleRequest(request); Collection<FormFile> files = multipartHandler.getFileElements().values(); Enumeration keys = multipartHandler.getFileElements().keys(); while (keys.hasMoreElements()) { Object key = keys.nextElement(); FormFile file = (FormFile) multipartHandler.getFileElements().get(key); long maxSize = WebUtils.getMaxUploadSize(form); if (LOG.isDebugEnabled()) { LOG.debug(file.getFileSize()); } if (maxSize > 0 && Long.parseLong(file.getFileSize() + "") > maxSize) { GlobalVariables.getMessageMap().putError(key.toString(), RiceKeyConstants.ERROR_UPLOADFILE_SIZE, new String[] { file.getFileName(), Long.toString(maxSize) }); } } // get file elements for kualirequestprocessor if (servletWrapper == null) { request.setAttribute(KRADConstants.UPLOADED_FILE_REQUEST_ATTRIBUTE_KEY, getFileParametersForMultipartRequest(request, multipartHandler)); } } } catch (ServletException e) { throw new ValidationException("unable to handle multipart request " + e.getMessage(), e); } } public static long getMaxUploadSize(ActionForm form) { long max = 0L; KualiMultipartRequestHandler multipartHandler = new KualiMultipartRequestHandler(); if (form instanceof PojoFormBase) { max = multipartHandler.calculateMaxUploadSizeToMaxOfList(((PojoFormBase) form).getMaxUploadSizes()); } if (LOG.isDebugEnabled()) { LOG.debug("Max File Upload Size: " + max); } return max; } private static Map getFileParametersForMultipartRequest(HttpServletRequest request, MultipartRequestHandler multipartHandler) { Map parameters = new HashMap(); Hashtable elements = multipartHandler.getFileElements(); Enumeration e = elements.keys(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); parameters.put(key, elements.get(key)); } if (request instanceof MultipartRequestWrapper) { request = (HttpServletRequest) ((MultipartRequestWrapper) request).getRequest(); e = request.getParameterNames(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); parameters.put(key, request.getParameterValues(key)); } } else { LOG.debug("Gathering multipart parameters for unwrapped request"); } return parameters; } public static void registerEditableProperty(PojoFormBase form, String editablePropertyName) { form.registerEditableProperty(editablePropertyName); } public static boolean isDocumentSession(Document document, PojoFormBase docForm) { boolean sessionDoc = document instanceof SessionDocument; boolean dataDictionarySessionDoc = false; if (!sessionDoc) { DataDictionary dataDictionary = KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary(); if (docForm instanceof KualiMaintenanceForm) { KualiMaintenanceForm maintenanceForm = (KualiMaintenanceForm) docForm; if (dataDictionary != null) { if (maintenanceForm.getDocTypeName() != null) { MaintenanceDocumentEntry maintenanceDocumentEntry = (MaintenanceDocumentEntry) dataDictionary .getDocumentEntry(maintenanceForm.getDocTypeName()); dataDictionarySessionDoc = maintenanceDocumentEntry.isSessionDocument(); } } } else { if (document != null && dataDictionary != null) { KNSDocumentEntry documentEntry = (KNSDocumentEntry) dataDictionary .getDocumentEntry(document.getClass().getName()); dataDictionarySessionDoc = documentEntry.isSessionDocument(); } } } return sessionDoc || dataDictionarySessionDoc; } public static boolean isFormSessionDocument(PojoFormBase form) { Document document = null; if (KualiDocumentFormBase.class.isAssignableFrom(form.getClass())) { KualiDocumentFormBase docForm = (KualiDocumentFormBase) form; document = docForm.getDocument(); } return isDocumentSession(document, form); } public static ActionForm getKualiForm(PageContext pageContext) { return getKualiForm((HttpServletRequest) pageContext.getRequest()); } public static ActionForm getKualiForm(HttpServletRequest request) { if (request.getAttribute(KEY_KUALI_FORM_IN_SESSION) != null) { return (ActionForm) request.getAttribute(KEY_KUALI_FORM_IN_SESSION); } else { final HttpSession session = request.getSession(false); return session != null ? (ActionForm) session.getAttribute(KEY_KUALI_FORM_IN_SESSION) : null; } } public static boolean isPropertyEditable(Set<String> editableProperties, String propertyName) { if (LOG.isDebugEnabled()) { LOG.debug("isPropertyEditable(" + propertyName + ")"); } boolean returnVal = editableProperties == null || editableProperties.contains(propertyName) || (getIndexOfCoordinateExtension(propertyName) == -1 ? false : editableProperties .contains(propertyName.substring(0, getIndexOfCoordinateExtension(propertyName)))); if (!returnVal) { if (LOG.isDebugEnabled()) { LOG.debug("isPropertyEditable(" + propertyName + ") == false / editableProperties: " + editableProperties); } } return returnVal; } public static boolean endsWithCoordinates(String parameter) { return parameter.endsWith(WebUtils.IMAGE_COORDINATE_CLICKED_X_EXTENSION) || parameter.endsWith(WebUtils.IMAGE_COORDINATE_CLICKED_Y_EXTENSION); } public static int getIndexOfCoordinateExtension(String parameter) { int indexOfCoordinateExtension = parameter.lastIndexOf(WebUtils.IMAGE_COORDINATE_CLICKED_X_EXTENSION); if (indexOfCoordinateExtension == -1) { indexOfCoordinateExtension = parameter.lastIndexOf(WebUtils.IMAGE_COORDINATE_CLICKED_Y_EXTENSION); } return indexOfCoordinateExtension; } public static boolean isInquiryHiddenField(String className, String fieldName, Object formObject, String propertyName) { boolean isHidden = false; String hiddenInquiryFields = getKualiConfigurationService().getPropertyValueAsString(className + ".hidden"); if (StringUtils.isEmpty(hiddenInquiryFields)) { return isHidden; } List hiddenFields = Arrays.asList(hiddenInquiryFields.replaceAll(" ", "").split(",")); if (hiddenFields.contains(fieldName.trim())) { isHidden = true; } return isHidden; } public static boolean isHiddenKimObjectType(String type, String configParameter) { boolean hideType = false; String hiddenTypes = getKualiConfigurationService().getPropertyValueAsString(configParameter); if (StringUtils.isEmpty(hiddenTypes)) { return hideType; } List hiddenTypeValues = Arrays.asList(hiddenTypes.replaceAll(" ", "").split(",")); if (hiddenTypeValues.contains(type.trim())) { hideType = true; } return hideType; } public static String getFullyMaskedValue(String className, String fieldName, Object formObject, String propertyName) { String displayMaskValue = null; Object propertyValue = ObjectUtils.getPropertyValue(formObject, propertyName); DataDictionaryEntryBase entry = (DataDictionaryEntryBase) KRADServiceLocatorWeb.getDataDictionaryService() .getDataDictionary().getDictionaryObjectEntry(className); AttributeDefinition a = entry.getAttributeDefinition(fieldName); AttributeSecurity attributeSecurity = a.getAttributeSecurity(); if (attributeSecurity != null && attributeSecurity.isMask()) { MaskFormatter maskFormatter = attributeSecurity.getMaskFormatter(); displayMaskValue = maskFormatter.maskValue(propertyValue); } return displayMaskValue; } public static String getPartiallyMaskedValue(String className, String fieldName, Object formObject, String propertyName) { String displayMaskValue = null; Object propertyValue = ObjectUtils.getPropertyValue(formObject, propertyName); DataDictionaryEntryBase entry = (DataDictionaryEntryBase) KRADServiceLocatorWeb.getDataDictionaryService() .getDataDictionary().getDictionaryObjectEntry(className); AttributeDefinition a = entry.getAttributeDefinition(fieldName); AttributeSecurity attributeSecurity = a.getAttributeSecurity(); if (attributeSecurity != null && attributeSecurity.isPartialMask()) { MaskFormatter partialMaskFormatter = attributeSecurity.getPartialMaskFormatter(); displayMaskValue = partialMaskFormatter.maskValue(propertyValue); } return displayMaskValue; } public static boolean canFullyUnmaskField(String businessObjectClassName, String fieldName, KualiForm form) { Class businessObjClass = null; try { businessObjClass = Class.forName(businessObjectClassName); } catch (Exception e) { throw new RuntimeException("Unable to resolve class name: " + businessObjectClassName); } if (form instanceof KualiDocumentFormBase) { return KNSServiceLocator.getBusinessObjectAuthorizationService().canFullyUnmaskField( GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName, ((KualiDocumentFormBase) form).getDocument()); } else { return KNSServiceLocator.getBusinessObjectAuthorizationService().canFullyUnmaskField( GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName, null); } } public static boolean canPartiallyUnmaskField(String businessObjectClassName, String fieldName, KualiForm form) { Class businessObjClass = null; try { businessObjClass = Class.forName(businessObjectClassName); } catch (Exception e) { throw new RuntimeException("Unable to resolve class name: " + businessObjectClassName); } if (form instanceof KualiDocumentFormBase) { return KNSServiceLocator.getBusinessObjectAuthorizationService().canPartiallyUnmaskField( GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName, ((KualiDocumentFormBase) form).getDocument()); } else { return KNSServiceLocator.getBusinessObjectAuthorizationService().canPartiallyUnmaskField( GlobalVariables.getUserSession().getPerson(), businessObjClass, fieldName, null); } } public static boolean canAddNoteAttachment(Document document) { boolean canViewNoteAttachment = false; DocumentAuthorizer documentAuthorizer = KNSServiceLocator.getDocumentHelperService() .getDocumentAuthorizer(document); canViewNoteAttachment = documentAuthorizer.canAddNoteAttachment(document, null, GlobalVariables.getUserSession().getPerson()); return canViewNoteAttachment; } public static boolean canViewNoteAttachment(Document document, String attachmentTypeCode) { boolean canViewNoteAttachment = false; DocumentAuthorizer documentAuthorizer = KNSServiceLocator.getDocumentHelperService() .getDocumentAuthorizer(document); canViewNoteAttachment = documentAuthorizer.canViewNoteAttachment(document, attachmentTypeCode, GlobalVariables.getUserSession().getPerson()); return canViewNoteAttachment; } public static boolean canDeleteNoteAttachment(Document document, String attachmentTypeCode, String authorUniversalIdentifier) { boolean canDeleteNoteAttachment = false; DocumentAuthorizer documentAuthorizer = KNSServiceLocator.getDocumentHelperService() .getDocumentAuthorizer(document); canDeleteNoteAttachment = documentAuthorizer.canDeleteNoteAttachment(document, attachmentTypeCode, "false", GlobalVariables.getUserSession().getPerson()); if (canDeleteNoteAttachment) { return canDeleteNoteAttachment; } else { canDeleteNoteAttachment = documentAuthorizer.canDeleteNoteAttachment(document, attachmentTypeCode, "true", GlobalVariables.getUserSession().getPerson()); if (canDeleteNoteAttachment && !authorUniversalIdentifier .equals(GlobalVariables.getUserSession().getPerson().getPrincipalId())) { canDeleteNoteAttachment = false; } } return canDeleteNoteAttachment; } public static void reuseErrorMapFromPreviousRequest(KualiDocumentFormBase kualiDocumentFormBase) { if (kualiDocumentFormBase.getMessageMapFromPreviousRequest() == null) { LOG.error("Error map from previous request is null!"); return; } MessageMap errorMapFromGlobalVariables = GlobalVariables.getMessageMap(); if (kualiDocumentFormBase.getMessageMapFromPreviousRequest() == errorMapFromGlobalVariables) { // if we've switched them already, then return early and do nothing return; } if (!errorMapFromGlobalVariables.hasNoErrors()) { throw new RuntimeException("Cannot replace error map because it is not empty"); } GlobalVariables.setMessageMap(kualiDocumentFormBase.getMessageMapFromPreviousRequest()); GlobalVariables.getMessageMap().clearErrorPath(); } /** * Excapes out HTML to prevent XSS attacks, and replaces the following * strings to allow for a limited set of HTML tags * <p> * <li>[X] and [/X], where X represents any 1 or 2 letter string may be used * to specify the equivalent tag in HTML (i.e. <X> and </X>) <li> * [font COLOR], where COLOR represents any valid html color (i.e. color * name or hexcode preceeded by #) will be filtered into <font * color="COLOR"/> <li>[/font] will be filtered into </font> <li> * [table CLASS], where CLASS gives the style class to use, will be filter * into <table class="CLASS"/> <li>[/table] will be filtered into * </table> <li>[td CLASS], where CLASS gives the style class to use, * will be filter into <td class="CLASS"/> * * @param inputString * @return */ public static String filterHtmlAndReplaceRiceMarkup(String inputString) { String outputString = StringEscapeUtils.escapeHtml4(inputString); // string has been escaped of all <, >, and & (and other characters) Map<String, String> findAndReplacePatterns = new LinkedHashMap<String, String>(); // now replace our rice custom markup into html // DON'T ALLOW THE SCRIPT TAG OR ARBITRARY IMAGES/URLS/ETC. THROUGH //strip out instances where javascript precedes a URL findAndReplacePatterns.put("\\[a ((javascript|JAVASCRIPT|JavaScript).+)\\]", ""); //turn passed a href value into appropriate tag findAndReplacePatterns.put("\\[a (.+)\\]", "<a href=\"$1\">"); findAndReplacePatterns.put("\\[/a\\]", "</a>"); // filter any one character tags findAndReplacePatterns.put("\\[([A-Za-z])\\]", "<$1>"); findAndReplacePatterns.put("\\[/([A-Za-z])\\]", "</$1>"); // filter any two character tags findAndReplacePatterns.put("\\[([A-Za-z]{2})\\]", "<$1>"); findAndReplacePatterns.put("\\[/([A-Za-z]{2})\\]", "</$1>"); // filter the font tag findAndReplacePatterns.put("\\[font (#[0-9A-Fa-f]{1,6}|[A-Za-z]+)\\]", "<font color=\"$1\">"); findAndReplacePatterns.put("\\[/font\\]", "</font>"); // filter the table tag findAndReplacePatterns.put("\\[table\\]", "<table>"); findAndReplacePatterns.put("\\[table ([A-Za-z]+)\\]", "<table class=\"$1\">"); findAndReplacePatterns.put("\\[/table\\]", "</table>"); // fiter td with class findAndReplacePatterns.put("\\[td ([A-Za-z]+)\\]", "<td class=\"$1\">"); for (String findPattern : findAndReplacePatterns.keySet()) { Pattern p = Pattern.compile(findPattern); Matcher m = p.matcher(outputString); if (m.find()) { String replacePattern = findAndReplacePatterns.get(findPattern); outputString = m.replaceAll(replacePattern); } } return outputString; } /** * Determines and returns the URL for question button images; looks first * for a property "application.custom.image.url", and if that is missing, * uses the image url returned by getDefaultButtonImageUrl() * * @param imageName the name of the image to find a button for * @return the URL where question button images are located */ public static String getButtonImageUrl(String imageName) { String buttonImageUrl = getKualiConfigurationService() .getPropertyValueAsString(WebUtils.APPLICATION_IMAGE_URL_PROPERTY_PREFIX + "." + imageName); if (StringUtils.isBlank(buttonImageUrl)) { buttonImageUrl = getDefaultButtonImageUrl(imageName); } return buttonImageUrl; } public static String getAttachmentImageForUrl(String contentType) { String image = getKualiConfigurationService() .getPropertyValueAsString(KRADConstants.ATTACHMENT_IMAGE_PREFIX + contentType); if (StringUtils.isEmpty(image)) { return getKualiConfigurationService().getPropertyValueAsString(KRADConstants.ATTACHMENT_IMAGE_DEFAULT); } return image; } /** * Generates a default button image URL, in the form of: * ${kr.externalizable.images.url}buttonsmall_${imageName}.gif * * @param imageName the image name to generate a default button name for * @return the default button image url */ public static String getDefaultButtonImageUrl(String imageName) { return getKualiConfigurationService().getPropertyValueAsString(WebUtils.DEFAULT_IMAGE_URL_PROPERTY_NAME) + "buttonsmall_" + imageName + ".gif"; } /** * @return an implementation of the KualiConfigurationService */ public static ConfigurationService getKualiConfigurationService() { if (configurationService == null) { configurationService = KRADServiceLocator.getKualiConfigurationService(); } return configurationService; } /** * Takes a string an converts the whitespace which would be ignored in an * HTML document into HTML elements so the whitespace is preserved * * @param startingString The string to preserve whitespace in * @return A string whose whitespace has been converted to HTML elements to preserve the whitespace in an HTML document */ public static String preserveWhitespace(String startingString) { String convertedString = startingString.replaceAll("\n", "<br />"); convertedString = convertedString.replaceAll(" ", " ").replaceAll("( | )", " "); return convertedString; } public static String getKimGroupDisplayName(String groupId) { if (StringUtils.isBlank(groupId)) { throw new IllegalArgumentException("Group ID must have a value"); } return KimApiServiceLocator.getGroupService().getGroup(groupId).getName(); } public static String getPrincipalDisplayName(String principalId) { if (StringUtils.isBlank(principalId)) { throw new IllegalArgumentException("Principal ID must have a value"); } if (KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId) == null) { return ""; } else { return KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId) .getDefaultName().getCompositeName(); } } /** * Takes an {@link org.kuali.rice.kew.api.action.ActionRequest} with a recipient type of * {@link org.kuali.rice.kew.api.action.RecipientType#ROLE} and returns the display name for the role. * * @param actionRequest the action request * @return the display name for the role * @throws IllegalArgumentException if the action request is null, or the recipient type is not ROLE */ public static String getRoleDisplayName(ActionRequest actionRequest) { String result; if (actionRequest == null) { throw new IllegalArgumentException("actionRequest must be non-null"); } if (RecipientType.ROLE != actionRequest.getRecipientType()) { throw new IllegalArgumentException("actionRequest recipient must be a Role"); } Role role = KimApiServiceLocator.getRoleService().getRole(actionRequest.getRoleName()); if (role != null) { result = role.getName(); } else if (!StringUtils.isBlank(actionRequest.getQualifiedRoleNameLabel())) { result = actionRequest.getQualifiedRoleNameLabel(); } else { result = actionRequest.getRoleName(); } return result; } /** * Returns an absolute URL which is a combination of a base part plus path, * or in the case that the path is already an absolute URL, the path alone * * @param base the url base path * @param path the path to append to base * @return an absolute URL representing the combination of base+path, or path alone if it is already absolute */ public static String toAbsoluteURL(String base, String path) { boolean abs = false; if (StringUtils.isBlank(path)) { path = ""; } else { for (String scheme : SCHEMES) { if (path.startsWith(scheme)) { abs = true; break; } } } if (abs) { return path; } return base + path; } public static String sanitizeBackLocation(String backLocation) { try { backLocation = urlCodec.decode(backLocation); Pattern pattern = Pattern.compile( ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.BACK_LOCATION_ALLOWED_REGEX)); if (StringUtils.isNotEmpty(backLocation) && pattern.matcher(backLocation).matches()) { return HtmlUtils.htmlEscape(backLocation); } } catch (DecoderException de) { LOG.debug("Failed to decode backLocation: " + backLocation, de); } return ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.BACK_LOCATION_DEFAULT_URL); } }