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