View Javadoc

1   /*
2    * Copyright 2005-2007 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kns.web.struts.action;
17  
18  import java.util.Arrays;
19  import java.util.Enumeration;
20  import java.util.HashMap;
21  import java.util.HashSet;
22  import java.util.Map;
23  import java.util.Properties;
24  import java.util.Set;
25  
26  import javax.servlet.http.HttpServletRequest;
27  import javax.servlet.http.HttpServletResponse;
28  
29  import org.apache.commons.lang.ObjectUtils;
30  import org.apache.commons.lang.StringUtils;
31  import org.apache.log4j.Level;
32  import org.apache.struts.action.ActionForm;
33  import org.apache.struts.action.ActionForward;
34  import org.apache.struts.action.ActionMapping;
35  import org.apache.struts.actions.DispatchAction;
36  import org.kuali.rice.core.service.Demonstration;
37  import org.kuali.rice.core.service.EncryptionService;
38  import org.kuali.rice.core.util.RiceConstants;
39  import org.kuali.rice.kim.bo.types.dto.AttributeSet;
40  import org.kuali.rice.kim.service.KIMServiceLocator;
41  import org.kuali.rice.kim.util.KimCommonUtils;
42  import org.kuali.rice.kim.util.KimConstants;
43  import org.kuali.rice.kns.bo.BusinessObject;
44  import org.kuali.rice.kns.document.authorization.DocumentAuthorizerBase;
45  import org.kuali.rice.kns.exception.AuthorizationException;
46  import org.kuali.rice.kns.lookup.LookupUtils;
47  import org.kuali.rice.kns.service.BusinessObjectAuthorizationService;
48  import org.kuali.rice.kns.service.KNSServiceLocator;
49  import org.kuali.rice.kns.service.KualiModuleService;
50  import org.kuali.rice.kns.service.ModuleService;
51  import org.kuali.rice.kns.util.GlobalVariables;
52  import org.kuali.rice.kns.util.KNSConstants;
53  import org.kuali.rice.kns.util.UrlFactory;
54  import org.kuali.rice.kns.util.WebUtils;
55  import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
56  import org.kuali.rice.kns.web.struts.form.KualiForm;
57  import org.kuali.rice.kns.web.struts.form.LookupForm;
58  import org.kuali.rice.kns.web.struts.pojo.PojoForm;
59  import org.kuali.rice.kns.web.struts.pojo.PojoFormBase;
60  
61  /**
62   * This class is the base action class for all kuali actions. Overrides execute to set methodToCall for image submits. Other setup
63   * for framework calls.
64   *
65   *
66   */
67  /**
68   * This is a description of what this class does - ctdang don't forget to fill this in. 
69   * 
70   * @author Kuali Rice Team (rice.collab@kuali.org)
71   *
72   */
73  public abstract class KualiAction extends DispatchAction {
74      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiAction.class);
75  
76      private static KualiModuleService kualiModuleService = null;
77      private static BusinessObjectAuthorizationService businessObjectAuthorizationService = null;
78      private static EncryptionService encryptionService = null;
79      private static Boolean OUTPUT_ENCRYPTION_WARNING = null;
80      private static String applicationBaseUrl = null;
81      
82      private Set<String> methodToCallsToNotCheckAuthorization = new HashSet<String>();
83      
84      {
85      	methodToCallsToNotCheckAuthorization.add( "performLookup" );
86      	methodToCallsToNotCheckAuthorization.add( "performQuestion" );
87      	methodToCallsToNotCheckAuthorization.add( "performQuestionWithInput" );
88      	methodToCallsToNotCheckAuthorization.add( "performQuestionWithInputAgainBecauseOfErrors" );
89      	methodToCallsToNotCheckAuthorization.add( "performQuestionWithoutInput" );
90      	methodToCallsToNotCheckAuthorization.add( "performWorkgroupLookup" );
91      }
92      
93      /**
94       * Entry point to all actions.
95       *
96       * NOTE: No need to hook into execute for handling framwork setup anymore. Just implement the methodToCall for the framework
97       * setup, Constants.METHOD_REQUEST_PARAMETER will contain the full parameter, which can be sub stringed for getting framework
98       * parameters.
99       *
100      * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm,
101      *      javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
102      */
103     public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
104         ActionForward returnForward = null;
105 
106         String methodToCall = findMethodToCall(form, request);
107         if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getMethodToCall())) {
108             if (StringUtils.isNotBlank(getImageContext(request, KNSConstants.ANCHOR))) {
109                 ((KualiForm) form).setAnchor(getImageContext(request, KNSConstants.ANCHOR));
110             }
111             else if (StringUtils.isNotBlank(request.getParameter(KNSConstants.ANCHOR))) {
112                 ((KualiForm) form).setAnchor(request.getParameter(KNSConstants.ANCHOR));
113             }
114             else {
115                 ((KualiForm) form).setAnchor(KNSConstants.ANCHOR_TOP_OF_FORM);
116             }
117         }
118         // if found methodToCall, pass control to that method, else return the basic forward
119         if (StringUtils.isNotBlank(methodToCall)) {
120         	if ( LOG.isDebugEnabled() ) {
121         		LOG.debug("methodToCall: '" + methodToCall+"'");
122         	}
123             returnForward = dispatchMethod(mapping, form, request, response, methodToCall);
124         }
125         else {
126             returnForward = defaultDispatch(mapping, form, request, response);
127         }
128 
129         // make sure the user can do what they're trying to according to the module that owns the functionality
130         if ( !methodToCallsToNotCheckAuthorization.contains(methodToCall) ) {
131         	if ( LOG.isDebugEnabled() ) {
132         		LOG.debug( "'" + methodToCall + "' not in set of excempt methods: " + methodToCallsToNotCheckAuthorization);
133         	}
134         	checkAuthorization(form, methodToCall);
135         } else {
136         	if ( LOG.isDebugEnabled() ) {
137         		LOG.debug("'" + methodToCall + "' is exempt from auth checks." );
138         	}
139         }
140 
141         // check if demonstration encryption is enabled
142         if ( LOG.isEnabledFor(Level.WARN) ) {
143 	        if ( OUTPUT_ENCRYPTION_WARNING == null ) {
144 	        	OUTPUT_ENCRYPTION_WARNING = KNSServiceLocator.getParameterService().getIndicatorParameter(KNSConstants.KNS_NAMESPACE, KNSConstants.DetailTypes.ALL_DETAIL_TYPE, KNSConstants.SystemGroupParameterNames.CHECK_ENCRYPTION_SERVICE_OVERRIDE_IND) && KNSServiceLocator.getEncryptionService() instanceof Demonstration; 
145 	        }
146 	        if ( OUTPUT_ENCRYPTION_WARNING.booleanValue() ) {
147 	            LOG.warn("WARNING: This implementation of Kuali uses the demonstration encryption framework.");
148 	        }
149         }
150 
151         // Add the ActionForm to GlobalVariables
152         // This will allow developers to retrieve both the Document and any request parameters that are not
153         // part of the Form and make them available in ValueFinder classes and other places where they are needed.
154         if(GlobalVariables.getKualiForm() == null) {
155         	GlobalVariables.setKualiForm((KualiForm)form);
156         }
157 
158         return returnForward;
159     }
160     
161     /**
162      * When no methodToCall is specified, the defaultDispatch method is invoked.  Default implementation
163      * returns the "basic" ActionForward.
164      */
165     protected ActionForward defaultDispatch(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
166     	return mapping.findForward(RiceConstants.MAPPING_BASIC);
167     }
168 
169     @Override
170     protected ActionForward dispatchMethod(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String methodToCall) throws Exception {
171         GlobalVariables.getUserSession().addObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_OBJECT_KEY, (Object)methodToCall);
172         return super.dispatchMethod(mapping, form, request, response, methodToCall);
173     }
174     
175     protected String findMethodToCall(ActionForm form, HttpServletRequest request) throws Exception {
176         String methodToCall;
177         if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getMethodToCall())) {
178             methodToCall = ((KualiForm) form).getMethodToCall();
179         }
180         else {
181             // call utility method to parse the methodToCall from the request.
182             methodToCall = WebUtils.parseMethodToCall(form, request);
183         }
184         return methodToCall;
185     }
186 
187     /**
188      * Toggles the tab state in the ui
189      *
190      * @param mapping
191      * @param form
192      * @param request
193      * @param response
194      * @return
195      * @throws Exception
196      */
197     public ActionForward toggleTab(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
198         KualiForm kualiForm = (KualiForm) form;
199         String tabToToggle = getTabToToggle(request);
200         if (StringUtils.isNotBlank(tabToToggle)) {
201             if (kualiForm.getTabState(tabToToggle).equals("OPEN")) {
202             	kualiForm.getTabStates().remove(tabToToggle);
203             	kualiForm.getTabStates().put(tabToToggle, "CLOSE");
204             }
205             else {
206             	kualiForm.getTabStates().remove(tabToToggle);
207             	kualiForm.getTabStates().put(tabToToggle, "OPEN");
208             }
209         }
210 
211         doProcessingAfterPost( kualiForm, request );
212         return mapping.findForward(RiceConstants.MAPPING_BASIC);
213     }
214 
215     /**
216      * Toggles all tabs to open
217      *
218      * @param mapping
219      * @param form
220      * @param request
221      * @param response
222      * @return
223      * @throws Exception
224      */
225     public ActionForward showAllTabs(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
226     	return this.doTabOpenOrClose(mapping, form, request, response, true);
227     }
228 
229     /**
230      * Toggles all tabs to closed
231      *
232      * @param mapping
233      * @param form
234      * @param request
235      * @param response
236      * @return
237      * @throws Exception
238      */
239     public ActionForward hideAllTabs(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
240         return this.doTabOpenOrClose(mapping, form, request, response, false);
241     }
242     
243     /**
244      * 
245      * Toggles all tabs to open of closed depending on the boolean flag.
246      * 
247      * @param mapping the mapping
248      * @param form the form
249      * @param request the request
250      * @param response the response
251      * @param open whether to open of close the tabs
252      * @return the action forward
253      */
254     private ActionForward doTabOpenOrClose(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, boolean open) {
255         KualiForm kualiForm = (KualiForm) form;
256 
257         Map<String, String> tabStates = kualiForm.getTabStates();
258         Map<String, String> newTabStates = new HashMap<String, String>();
259         for (String tabKey: tabStates.keySet()) {
260         	newTabStates.put(tabKey, open ? "OPEN" : "CLOSE");
261         }
262         kualiForm.setTabStates(newTabStates);
263         doProcessingAfterPost( kualiForm, request );
264         return mapping.findForward(RiceConstants.MAPPING_BASIC);
265     }
266 
267     /**
268      * Default refresh method. Called from returning frameworks.
269      *
270      * @param mapping
271      * @param form
272      * @param request
273      * @param response
274      * @return
275      * @throws Exception
276      */
277     public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
278         return mapping.findForward(RiceConstants.MAPPING_BASIC);
279     }
280 
281 
282     /**
283      * Parses the method to call attribute to pick off the line number which should be deleted.
284      *
285      * @param request
286      * @return
287      */
288     protected int getLineToDelete(HttpServletRequest request) {
289         return getSelectedLine(request);
290     }
291 
292     /**
293      * Parses the method to call attribute to pick off the line number which should have an action performed on it.
294      *
295      * @param request
296      * @return
297      */
298     protected int getSelectedLine(HttpServletRequest request) {
299         int selectedLine = -1;
300         String parameterName = (String) request.getAttribute(KNSConstants.METHOD_TO_CALL_ATTRIBUTE);
301         if (StringUtils.isNotBlank(parameterName)) {
302             String lineNumber = StringUtils.substringBetween(parameterName, ".line", ".");
303             selectedLine = Integer.parseInt(lineNumber);
304         }
305 
306         return selectedLine;
307     }
308 
309     /**
310      * Determines which tab was requested to be toggled
311      *
312      * @param request
313      * @return
314      */
315     protected String getTabToToggle(HttpServletRequest request) {
316         String tabToToggle = "";
317         String parameterName = (String) request.getAttribute(KNSConstants.METHOD_TO_CALL_ATTRIBUTE);
318         if (StringUtils.isNotBlank(parameterName)) {
319             tabToToggle = StringUtils.substringBetween(parameterName, ".tab", ".");
320         }
321 
322         return tabToToggle;
323     }
324 
325     /**
326      * Retrieves the header tab to navigate to.
327      *
328      * @param request
329      * @return
330      */
331     protected String getHeaderTabNavigateTo(HttpServletRequest request) {
332         String headerTabNavigateTo = RiceConstants.MAPPING_BASIC;
333         String imageContext = getImageContext(request, KNSConstants.NAVIGATE_TO);
334         if (StringUtils.isNotBlank(imageContext)) {
335             headerTabNavigateTo = imageContext;
336         }
337         return headerTabNavigateTo;
338     }
339 
340     /**
341      * Retrieves the header tab dispatch.
342      *
343      * @param request
344      * @return
345      */
346     protected String getHeaderTabDispatch(HttpServletRequest request) {
347         String headerTabDispatch = null;
348         String imageContext = getImageContext(request, KNSConstants.HEADER_DISPATCH);
349         if (StringUtils.isNotBlank(imageContext)) {
350             headerTabDispatch = imageContext;
351         }
352         else {
353             // In some cases it might be in request params instead
354             headerTabDispatch = request.getParameter(KNSConstants.METHOD_TO_CALL_ATTRIBUTE);
355         }
356         return headerTabDispatch;
357     }
358 
359     /**
360      * Retrieves the image context
361      *
362      * @param request
363      * @param contextKey
364      * @return
365      */
366     protected String getImageContext(HttpServletRequest request, String contextKey) {
367         String imageContext = "";
368         String parameterName = (String) request.getAttribute(KNSConstants.METHOD_TO_CALL_ATTRIBUTE);
369         if (StringUtils.isBlank(parameterName)) {
370             parameterName = request.getParameter("methodToCallPath");
371         }
372         if (StringUtils.isNotBlank(parameterName)) {
373             imageContext = StringUtils.substringBetween(parameterName, contextKey, ".");
374         }
375         return imageContext;
376     }
377 
378     protected String getReturnLocation(HttpServletRequest request, ActionMapping mapping) {
379     	String mappingPath = mapping.getPath();
380     	String basePath = getApplicationBaseUrl();
381         return basePath + ("/lookup".equals(mappingPath) || "/maintenance".equals(mappingPath) || "/multipleValueLookup".equals(mappingPath) ? "/kr" : "") + mappingPath + ".do";
382     }
383 
384     /**
385      * Retrieves the value of a parameter to be passed into the lookup or inquiry frameworks.  The default implementation of this method will attempt to look
386      * in the request to determine wheter the appropriate value exists as a request parameter.  If not, it will attempt to look through the form object to find
387      * the property.
388      * 
389      * @param boClass a class implementing boClass, representing the BO that will be looked up
390      * @param parameterName the name of the parameter
391      * @param parameterValuePropertyName the property (relative to the form object) where the value to be passed into the lookup/inquiry may be found
392      * @param form
393      * @param request
394      * @return
395      */
396     protected String retrieveLookupParameterValue(Class<? extends BusinessObject> boClass, String parameterName, String parameterValuePropertyName, ActionForm form, HttpServletRequest request) throws Exception {
397     	String value;
398     	if (StringUtils.contains(parameterValuePropertyName, "'")) {
399     		value = StringUtils.replace(parameterValuePropertyName, "'", "");
400     	}
401     	else if (request.getParameterMap().containsKey(parameterValuePropertyName)) {
402     		value = request.getParameter(parameterValuePropertyName);
403     	}
404     	else {
405     		if (form instanceof KualiForm) {
406     			value = ((KualiForm) form).retrieveFormValueForLookupInquiryParameters(parameterName, parameterValuePropertyName);
407     		} else {
408 	    		if (LOG.isDebugEnabled()) {
409 	    			LOG.debug("Unable to retrieve lookup/inquiry parameter value for parameter name " + parameterName + " parameter value property " + parameterValuePropertyName);
410 	    		}
411 	    		value = null;
412     		}
413     	}
414     	
415     	if (value != null && boClass != null && getBusinessObjectAuthorizationService().attributeValueNeedsToBeEncryptedOnFormsAndLinks(boClass, parameterName)) {
416     		value = getEncryptionService().encrypt(value) + EncryptionService.ENCRYPTION_POST_PREFIX;
417     	}
418     	return value;
419     }
420     
421     /**
422      * Takes care of storing the action form in the User session and forwarding to the lookup action.
423      *
424      * @param mapping
425      * @param form
426      * @param request
427      * @param response
428      * @return
429      * @throws Exception
430      */
431     @SuppressWarnings("unchecked")
432 	public ActionForward performLookup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
433         // parse out the important strings from our methodToCall parameter
434         String fullParameter = (String) request.getAttribute(KNSConstants.METHOD_TO_CALL_ATTRIBUTE);
435         validateLookupInquiryFullParameter(request, form, fullParameter);
436         
437         KualiForm kualiForm = (KualiForm) form;
438         
439         // when we return from the lookup, our next request's method to call is going to be refresh
440         kualiForm.registerEditableProperty(KNSConstants.DISPATCH_REQUEST_PARAMETER);
441         
442         // parse out the baseLookupUrl if there is one
443         String baseLookupUrl = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM14_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM14_RIGHT_DEL);
444         
445         // parse out business object class name for lookup
446         String boClassName = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL, KNSConstants.METHOD_TO_CALL_BOPARM_RIGHT_DEL);
447         if (StringUtils.isBlank(boClassName)) {
448             throw new RuntimeException("Illegal call to perform lookup, no business object class name specified.");
449         }
450         Class boClass = null;
451 		try{
452 			boClass = Class.forName(boClassName);
453 		} catch(ClassNotFoundException cnfex){
454 			// we must have a valid boClass that can be loaded unless baseLookupUrl is defined
455 			if (StringUtils.isBlank(baseLookupUrl)) {
456 				if ( LOG.isDebugEnabled() ) {
457 					LOG.debug( "BO Class " + boClassName + " not found in the current context, checking the RiceApplicationConfigurationService." );
458 				}
459 				baseLookupUrl = KNSServiceLocator.getRiceApplicationConfigurationMediationService().getBaseLookupUrl(boClassName);
460 				if ( LOG.isDebugEnabled() ) {
461 					LOG.debug( "URL Returned from KSB: " + baseLookupUrl );
462 				}
463 				if ( StringUtils.isBlank(baseLookupUrl)) {
464 					throw new IllegalArgumentException("The classname (" + boClassName + ") does not represent a valid class and no base URL could be found on the bus.");
465 				}
466 			}
467 		}
468 		
469         // build the parameters for the lookup url
470         Properties parameters = new Properties();
471         String conversionFields = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL);
472         if (StringUtils.isNotBlank(conversionFields)) {
473             parameters.put(KNSConstants.CONVERSION_FIELDS_PARAMETER, conversionFields);
474             
475             // register each of the destination parameters of the field conversion string as editable
476             String[] fieldConversions = conversionFields.split(KNSConstants.FIELD_CONVERSIONS_SEPARATOR);
477             for (int i = 0; i < fieldConversions.length; i++) {
478             	String destination = fieldConversions[i].split(KNSConstants.FIELD_CONVERSION_PAIR_SEPARATOR)[1];
479             	kualiForm.registerEditableProperty(destination);
480             }
481         }
482 
483         // pass values from form that should be pre-populated on lookup search
484         String parameterFields = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM2_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM2_RIGHT_DEL);
485         if ( LOG.isDebugEnabled() ) {
486             LOG.debug( "fullParameter: " + fullParameter );
487             LOG.debug( "parameterFields: " + parameterFields );
488         }
489         if (StringUtils.isNotBlank(parameterFields)) {
490             String[] lookupParams = parameterFields.split(KNSConstants.FIELD_CONVERSIONS_SEPARATOR);
491             if ( LOG.isDebugEnabled() ) {
492             	 LOG.debug( "lookupParams: " + Arrays.toString(lookupParams) ); 
493             }
494             for (int i = 0; i < lookupParams.length; i++) {
495             	String[] keyValue = lookupParams[i].split(KNSConstants.FIELD_CONVERSION_PAIR_SEPARATOR); 
496                 if (keyValue.length != 2) throw new RuntimeException("malformed field conversion pair: " + Arrays.toString(keyValue)); 
497 
498                 String lookupParameterValue = retrieveLookupParameterValue(boClass, keyValue[1], keyValue[0], form, request);
499                 if (StringUtils.isNotBlank(lookupParameterValue)) {
500                 	parameters.put(keyValue[1], lookupParameterValue);
501                 }
502 
503                 if ( LOG.isDebugEnabled() ) {
504                     LOG.debug( "keyValue[0]: " + keyValue[0] );
505                     LOG.debug( "keyValue[1]: " + keyValue[1] );
506                 }
507             }
508         }
509 
510         // pass values from form that should be read-Only on lookup search
511         String readOnlyFields = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM8_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM8_RIGHT_DEL);
512         if (StringUtils.isNotBlank(readOnlyFields)) {
513             parameters.put(KNSConstants.LOOKUP_READ_ONLY_FIELDS, readOnlyFields);
514         }
515 
516         if ( LOG.isDebugEnabled() ) {
517             LOG.debug( "fullParameter: " + fullParameter );
518             LOG.debug( "readOnlyFields: " + readOnlyFields );
519         }
520 
521         // grab whether or not the "return value" link should be hidden or not
522         String hideReturnLink = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM3_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM3_RIGHT_DEL);
523         if (StringUtils.isNotBlank(hideReturnLink)) {
524             parameters.put(KNSConstants.HIDE_LOOKUP_RETURN_LINK, hideReturnLink);
525         }
526 
527         // add the optional extra button source and parameters string
528         String extraButtonSource = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM4_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM4_RIGHT_DEL);
529         if (StringUtils.isNotBlank(extraButtonSource)) {
530             parameters.put(KNSConstants.EXTRA_BUTTON_SOURCE, extraButtonSource);
531         }
532         String extraButtonParams = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM5_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM5_RIGHT_DEL);
533         if (StringUtils.isNotBlank(extraButtonParams)) {
534             parameters.put(KNSConstants.EXTRA_BUTTON_PARAMS, extraButtonParams);
535         }
536 
537         String lookupAction = KNSConstants.LOOKUP_ACTION;
538 
539         // is this a multi-value return?
540         boolean isMultipleValue = false;
541         String multipleValues = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM6_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM6_RIGHT_DEL);
542         if ((new Boolean(multipleValues).booleanValue())) {
543             parameters.put(KNSConstants.MULTIPLE_VALUE, multipleValues);
544             lookupAction = KNSConstants.MULTIPLE_VALUE_LOOKUP_ACTION;
545             isMultipleValue = true;
546         }
547 
548         // the name of the collection being looked up (primarily for multivalue lookups
549         String lookedUpCollectionName = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM11_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM11_RIGHT_DEL);
550         if (StringUtils.isNotBlank(lookedUpCollectionName)) {
551             parameters.put(KNSConstants.LOOKED_UP_COLLECTION_NAME, lookedUpCollectionName);
552         }
553 
554         // grab whether or not the "supress actions" column should be hidden or not
555         String supressActions = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM7_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM7_RIGHT_DEL);
556         if (StringUtils.isNotBlank(supressActions)) {
557             parameters.put(KNSConstants.SUPPRESS_ACTIONS, supressActions);
558         }
559 
560         // grab the references that should be refreshed upon returning from the lookup
561         String referencesToRefresh = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM10_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM10_RIGHT_DEL);
562         if (StringUtils.isNotBlank(referencesToRefresh)) {
563             parameters.put(KNSConstants.REFERENCES_TO_REFRESH, referencesToRefresh);
564         }
565 
566         // anchor, if it exists
567         if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
568             parameters.put(KNSConstants.LOOKUP_ANCHOR, ((KualiForm) form).getAnchor());
569         }
570 
571         // now add required parameters
572         parameters.put(KNSConstants.DISPATCH_REQUEST_PARAMETER, "start");
573 
574         // pass value from form that shows if autoSearch is desired for lookup search
575         String autoSearch = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM9_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM9_RIGHT_DEL);
576 
577         if (StringUtils.isNotBlank(autoSearch)) {
578             parameters.put(KNSConstants.LOOKUP_AUTO_SEARCH, autoSearch);
579             if ("YES".equalsIgnoreCase(autoSearch)){
580                 parameters.put(KNSConstants.DISPATCH_REQUEST_PARAMETER, "search");
581             }
582         }
583 
584         parameters.put(KNSConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObject(form));
585         parameters.put(KNSConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, boClassName);
586 
587         parameters.put(KNSConstants.RETURN_LOCATION_PARAMETER, getReturnLocation(request, mapping));
588         
589     	if (form instanceof KualiDocumentFormBase) {
590     		String docNum = ((KualiDocumentFormBase) form).getDocument().getDocumentNumber();
591 			if(docNum != null){
592 				parameters.put(KNSConstants.DOC_NUM, docNum);
593 			}
594 		}else if(form instanceof LookupForm){
595 			String docNum = ((LookupForm) form).getDocNum();
596 			if(docNum != null){
597 				parameters.put(KNSConstants.DOC_NUM, ((LookupForm) form).getDocNum());
598 			}
599     	}
600 
601     	if (boClass != null) {
602     		ModuleService responsibleModuleService = getKualiModuleService().getResponsibleModuleService(boClass);
603     		if(responsibleModuleService!=null && responsibleModuleService.isExternalizable(boClass)){
604     			Map<String, String> parameterMap = new HashMap<String, String>();
605     			Enumeration<Object> e = parameters.keys();
606     			while (e.hasMoreElements()) {
607     				String paramName = (String) e.nextElement();
608     				parameterMap.put(paramName, parameters.getProperty(paramName));
609     			}
610     			return new ActionForward(responsibleModuleService.getExternalizableBusinessObjectLookupUrl(boClass, parameterMap), true);
611     		}
612     	}
613 		
614     	if (StringUtils.isBlank(baseLookupUrl)) {
615     		baseLookupUrl = getApplicationBaseUrl() + "/kr/" + lookupAction;
616     	} else {
617     		if (isMultipleValue) {
618     			LookupUtils.transformLookupUrlToMultiple(baseLookupUrl);
619     		}
620     	}
621     	String lookupUrl = UrlFactory.parameterizeUrl(baseLookupUrl, parameters);
622         return new ActionForward(lookupUrl, true);
623     }
624 
625     protected void validateLookupInquiryFullParameter(HttpServletRequest request, ActionForm form, String fullParameter){
626     	PojoFormBase pojoFormBase = (PojoFormBase) form;
627         if(WebUtils.isFormSessionDocument((PojoFormBase)form)){
628         	if(!pojoFormBase.isPropertyEditable(fullParameter)) {
629         		throw new RuntimeException("The methodToCallAttribute is not registered as an editable property.");
630         	}
631         }
632     }
633     
634     @SuppressWarnings("unchecked")
635 	public ActionForward performInquiry(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
636         // parse out the important strings from our methodToCall parameter
637         String fullParameter = (String) request.getAttribute(KNSConstants.METHOD_TO_CALL_ATTRIBUTE);
638         validateLookupInquiryFullParameter(request, form, fullParameter);
639         
640         // when javascript is disabled, the inquiry will appear in the same window as the document.  when we close the inquiry, 
641         // our next request's method to call is going to be refresh
642         KualiForm kualiForm = (KualiForm) form;
643         kualiForm.registerEditableProperty(KNSConstants.DISPATCH_REQUEST_PARAMETER);
644         
645         // parse out business object class name for lookup
646         String boClassName = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL, KNSConstants.METHOD_TO_CALL_BOPARM_RIGHT_DEL);
647         if (StringUtils.isBlank(boClassName)) {
648             throw new RuntimeException("Illegal call to perform inquiry, no business object class name specified.");
649         }
650 
651         // build the parameters for the inquiry url
652         Properties parameters = new Properties();
653         parameters.put(KNSConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, boClassName);
654 
655         parameters.put(KNSConstants.RETURN_LOCATION_PARAMETER, getReturnLocation(request, mapping));
656 
657         // pass values from form that should be pre-populated on inquiry
658         String parameterFields = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM2_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM2_RIGHT_DEL);
659         if ( LOG.isDebugEnabled() ) {
660             LOG.debug( "fullParameter: " + fullParameter );
661             LOG.debug( "parameterFields: " + parameterFields );
662         }
663         if (StringUtils.isNotBlank(parameterFields)) {
664             // TODO : create a method for this to be used by both lookup & inquiry ?
665             String[] inquiryParams = parameterFields.split(KNSConstants.FIELD_CONVERSIONS_SEPARATOR);
666             if ( LOG.isDebugEnabled() ) {
667                 LOG.debug( "inquiryParams: " + inquiryParams );
668             }
669             Class<? extends BusinessObject> boClass = (Class<? extends BusinessObject>) Class.forName(boClassName);
670             for (int i = 0; i < inquiryParams.length; i++) {
671                 String[] keyValue = inquiryParams[i].split(KNSConstants.FIELD_CONVERSION_PAIR_SEPARATOR);
672 
673                 String inquiryParameterValue = retrieveLookupParameterValue(boClass, keyValue[1], keyValue[0], form, request);
674                 if (inquiryParameterValue == null) {
675                 	parameters.put(keyValue[1], "directInquiryKeyNotSpecified");
676                 }
677                 else {
678                 	parameters.put(keyValue[1], inquiryParameterValue);
679                 }
680 
681                 if ( LOG.isDebugEnabled() ) {
682                     LOG.debug( "keyValue[0]: " + keyValue[0] );
683                     LOG.debug( "keyValue[1]: " + keyValue[1] );
684                 }
685             }
686         }
687         parameters.put(KNSConstants.DISPATCH_REQUEST_PARAMETER, "start");
688         parameters.put(KNSConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObject(form));
689         String inquiryUrl = null;
690         try {
691         	Class.forName(boClassName);
692         	inquiryUrl = getApplicationBaseUrl() + "/kr/" + KNSConstants.DIRECT_INQUIRY_ACTION;
693         } catch ( ClassNotFoundException ex ) {
694         	inquiryUrl = KNSServiceLocator.getRiceApplicationConfigurationMediationService().getBaseInquiryUrl(boClassName);
695         }
696         inquiryUrl = UrlFactory.parameterizeUrl(inquiryUrl, parameters);
697         return new ActionForward(inquiryUrl, true);
698 
699     }
700 
701     /**
702      * This method handles rendering the question component, but without any of the extra error fields
703      *
704      * @param mapping
705      * @param form
706      * @param request
707      * @param response
708      * @param questionId
709      * @param questionText
710      * @param questionType
711      * @param caller
712      * @param context
713      * @return ActionForward
714      * @throws Exception
715      */
716     protected ActionForward performQuestionWithoutInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionId, String questionText, String questionType, String caller, String context) throws Exception {
717         return performQuestion(mapping, form, request, response, questionId, questionText, questionType, caller, context, false, "", "", "", "");
718     }
719 
720     /**
721      * Handles rendering a question prompt - without a specified context.
722      *
723      * @param mapping
724      * @param form
725      * @param request
726      * @param response
727      * @param questionId
728      * @param questionText
729      * @param questionType
730      * @param caller
731      * @param context
732      * @return ActionForward
733      * @throws Exception
734      */
735     protected ActionForward performQuestionWithInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionId, String questionText, String questionType, String caller, String context) throws Exception {
736         return performQuestion(mapping, form, request, response, questionId, questionText, questionType, caller, context, true, "", "", "", "");
737     }
738 
739     /**
740      * Handles re-rendering a question prompt because of an error on what was submitted.
741      *
742      * @param mapping
743      * @param form
744      * @param request
745      * @param response
746      * @param questionId
747      * @param questionText
748      * @param questionType
749      * @param caller
750      * @param context
751      * @param reason
752      * @param errorKey
753      * @param errorPropertyName
754      * @param errorParameter
755      * @return ActionForward
756      * @throws Exception
757      */
758     protected ActionForward performQuestionWithInputAgainBecauseOfErrors(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionId, String questionText, String questionType, String caller, String context, String reason, String errorKey, String errorPropertyName, String errorParameter) throws Exception {
759         return performQuestion(mapping, form, request, response, questionId, questionText, questionType, caller, context, true, reason, errorKey, errorPropertyName, errorParameter);
760     }
761 
762     /**
763      * Handles rendering a question prompt - with a specified context.
764      *
765      * @param mapping
766      * @param form
767      * @param request
768      * @param response
769      * @param questionId
770      * @param questionText
771      * @param questionType
772      * @param caller
773      * @param context
774      * @param showReasonField
775      * @param reason
776      * @param errorKey
777      * @param errorPropertyName
778      * @param errorParameter
779      * @return ActionForward
780      * @throws Exception
781      */
782     private ActionForward performQuestion(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionId, String questionText, String questionType, String caller, String context, boolean showReasonField, String reason, String errorKey, String errorPropertyName, String errorParameter) throws Exception {
783         Properties parameters = new Properties();
784 
785         parameters.put(KNSConstants.DISPATCH_REQUEST_PARAMETER, "start");
786         parameters.put(KNSConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObject(form));
787         parameters.put(KNSConstants.CALLING_METHOD, caller);
788         parameters.put(KNSConstants.QUESTION_INST_ATTRIBUTE_NAME, questionId);
789         parameters.put(KNSConstants.QUESTION_IMPL_ATTRIBUTE_NAME, questionType);
790         parameters.put(KNSConstants.QUESTION_TEXT_ATTRIBUTE_NAME, questionText);
791         parameters.put(KNSConstants.RETURN_LOCATION_PARAMETER, getReturnLocation(request, mapping));
792         parameters.put(KNSConstants.QUESTION_CONTEXT, context);
793         parameters.put(KNSConstants.QUESTION_SHOW_REASON_FIELD, Boolean.toString(showReasonField));
794         parameters.put(KNSConstants.QUESTION_REASON_ATTRIBUTE_NAME, reason);
795         parameters.put(KNSConstants.QUESTION_ERROR_KEY, errorKey);
796         parameters.put(KNSConstants.QUESTION_ERROR_PROPERTY_NAME, errorPropertyName);
797         parameters.put(KNSConstants.QUESTION_ERROR_PARAMETER, errorParameter);
798         parameters.put(KNSConstants.QUESTION_ANCHOR, form instanceof KualiForm ? ObjectUtils.toString(((KualiForm) form).getAnchor()) : "");
799         Object methodToCallAttribute = request.getAttribute(KNSConstants.METHOD_TO_CALL_ATTRIBUTE);
800         if (methodToCallAttribute != null) {
801             parameters.put(KNSConstants.METHOD_TO_CALL_PATH, methodToCallAttribute);
802             ((PojoForm) form).registerEditableProperty(String.valueOf(methodToCallAttribute));
803         }
804         
805     	if (form instanceof KualiDocumentFormBase) {
806     		String docNum = ((KualiDocumentFormBase) form).getDocument().getDocumentNumber();
807     		if(docNum != null){
808     			parameters.put(KNSConstants.DOC_NUM, ((KualiDocumentFormBase) form)
809 					.getDocument().getDocumentNumber());
810     		}
811 		}
812 
813         String questionUrl = UrlFactory.parameterizeUrl(getApplicationBaseUrl() + "/kr/" + KNSConstants.QUESTION_ACTION, parameters);
814         return new ActionForward(questionUrl, true);
815     }
816 
817 
818     /**
819      * Takes care of storing the action form in the User session and forwarding to the workflow workgroup lookup action.
820      *
821      * @param mapping
822      * @param form
823      * @param request
824      * @param response
825      * @return
826      * @throws Exception
827      */
828     public ActionForward performWorkgroupLookup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
829     	String returnUrl = null;
830     	if ("/kr".equals(mapping.getModuleConfig().getPrefix())) {
831     		returnUrl = getApplicationBaseUrl() + mapping.getModuleConfig().getPrefix() + mapping.getPath() + ".do";
832     	} else {
833     		returnUrl = getApplicationBaseUrl() + mapping.getPath() + ".do";
834     	}
835 
836 
837         String fullParameter = (String) request.getAttribute(KNSConstants.METHOD_TO_CALL_ATTRIBUTE);
838         String conversionFields = StringUtils.substringBetween(fullParameter, KNSConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KNSConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL);
839 
840         String deploymentBaseUrl = KNSServiceLocator.getKualiConfigurationService().getPropertyString(KNSConstants.WORKFLOW_URL_KEY);
841         String workgroupLookupUrl = deploymentBaseUrl + "/Lookup.do?lookupableImplServiceName=WorkGroupLookupableImplService&methodToCall=start&docFormKey=" + GlobalVariables.getUserSession().addObject(form);
842 
843         if (conversionFields != null) {
844             workgroupLookupUrl += "&conversionFields=" + conversionFields;
845         }
846     	if (form instanceof KualiDocumentFormBase) {
847 			workgroupLookupUrl +="&docNum="+ ((KualiDocumentFormBase) form).getDocument().getDocumentNumber();
848 		}
849     	
850         workgroupLookupUrl += "&returnLocation=" + returnUrl;
851 
852         return new ActionForward(workgroupLookupUrl, true);
853     }
854 
855     /**
856      * Handles requests that originate via Header Tabs.
857      *
858      * @param mapping
859      * @param form
860      * @param request
861      * @param response
862      * @return
863      * @throws Exception
864      */
865     public ActionForward headerTab(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
866         // header tab actions can do two things - 1, call into an action and perform what needs to happen in there and 2, forward to
867         // a new location.
868         String headerTabDispatch = getHeaderTabDispatch(request);
869         if (StringUtils.isNotEmpty(headerTabDispatch)) {
870             ActionForward forward = dispatchMethod(mapping, form, request, response, headerTabDispatch);
871             if (GlobalVariables.getMessageMap().getNumberOfPropertiesWithErrors() > 0) {
872                 return mapping.findForward(RiceConstants.MAPPING_BASIC);
873             }
874             this.doTabOpenOrClose(mapping, form, request, response, false);
875             if (forward.getRedirect()) {
876                 return forward;
877             }
878         }
879         return dispatchMethod(mapping, form, request, response, getHeaderTabNavigateTo(request));
880     }
881 
882     /**
883      * Override this method to provide action-level access controls to the application.
884      *
885      * @param form
886      * @throws AuthorizationException
887      */
888     protected void checkAuthorization( ActionForm form, String methodToCall) throws AuthorizationException 
889     {
890     	String principalId = GlobalVariables.getUserSession().getPrincipalId();
891     	AttributeSet roleQualifier = new AttributeSet(getRoleQualification(form, methodToCall));
892     	AttributeSet permissionDetails = KimCommonUtils.getNamespaceAndActionClass(this.getClass());
893     	
894         if (!KIMServiceLocator.getIdentityManagementService().isAuthorizedByTemplateName(principalId, KNSConstants.KNS_NAMESPACE, 
895         		KimConstants.PermissionTemplateNames.USE_SCREEN, permissionDetails, roleQualifier )) 
896         {
897             throw new AuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(), 
898             		methodToCall,
899             		this.getClass().getSimpleName());
900         }
901     }
902     
903     /** 
904      * override this method to add data from the form for role qualification in the authorization check
905      */
906     protected Map<String,String> getRoleQualification(ActionForm form, String methodToCall) {
907     	return new HashMap<String,String>();
908     }
909 
910     protected static KualiModuleService getKualiModuleService() {
911         if ( kualiModuleService == null ) {
912             kualiModuleService = KNSServiceLocator.getKualiModuleService();
913         }
914         return kualiModuleService;
915     }
916 
917     /**
918      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
919      * <p>Value is textAreaFieldName
920      */
921     public static final String TEXT_AREA_FIELD_NAME="textAreaFieldName";
922     /**
923      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
924      * <p>Value is textAreaFieldLabel
925     */
926     public static final String TEXT_AREA_FIELD_LABEL="textAreaFieldLabel";
927     /**
928      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
929      * <p>Value is textAreaReadOnly
930     */
931     public static final String TEXT_AREA_READ_ONLY="textAreaReadOnly";
932     /**
933      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
934      * <p>Value is textAreaFieldAnchor
935     */
936     public static final String TEXT_AREA_FIELD_ANCHOR="textAreaFieldAnchor";
937     /**
938      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
939      * <p>Value is textAreaFieldAnchor
940     */
941     public static final String TEXT_AREA_MAX_LENGTH="textAreaMaxLength";
942     /**
943      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
944      * <p>Value is htmlFormAction
945     */
946     public static final String FORM_ACTION="htmlFormAction";
947     /**
948      * Constant defined to match input parameter from URL and from TextArea.jsp.
949      * <p>Value is methodToCall
950     */
951     public static final String METHOD_TO_CALL="methodToCall";
952     /**
953      * Constant defined to match with global forwarding in struts-config.xml
954      * for Text Area Update.
955      * <p>Value is updateTextArea
956     */
957     public static final String FORWARD_TEXT_AREA_UPDATE="updateTextArea";
958     /**
959      * Constant defined to match with method to call in TextArea.jsp.
960      * <p>Value is postTextAreaToParent
961     */
962     public static final String POST_TEXT_AREA_TO_PARENT="postTextAreaToParent";
963     /**
964      * Constant defined to match with local forwarding in struts-config.xml
965      * for the parent of the Updated Text Area.
966      * <p>Value is forwardNext
967     */
968     public static final String FORWARD_NEXT="forwardNext";
969 
970     /**
971      * This method is invoked when Java Script is turned off from the web browser. It
972      * setup the information that the update text area requires for copying current text
973      * in the calling page text area and returning to the calling page. The information
974      * is passed to the JSP through Http Request attributes. All other parameters are
975      * forwarded 
976      *  
977      * @param mapping
978      * @param form
979      * @param request
980      * @param response
981      * @return
982      */
983     public ActionForward updateTextArea(ActionMapping mapping,
984             ActionForm form,
985             HttpServletRequest request,
986             HttpServletResponse response)  {
987         if (LOG.isTraceEnabled()) {
988             String lm=String.format("ENTRY %s%n%s", form.getClass().getSimpleName(),
989                     request.getRequestURI());
990             LOG.trace(lm);
991         }
992                                 
993         final String[] keyValue = getTextAreaParams(request);
994         
995         request.setAttribute(TEXT_AREA_FIELD_NAME, keyValue[0]);
996         request.setAttribute(FORM_ACTION,keyValue[1]);
997         request.setAttribute(TEXT_AREA_FIELD_LABEL,keyValue[2]);
998         request.setAttribute(TEXT_AREA_READ_ONLY,keyValue[3]);
999         request.setAttribute(TEXT_AREA_MAX_LENGTH,keyValue[4]);
1000         if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
1001             request.setAttribute(TEXT_AREA_FIELD_ANCHOR,((KualiForm) form).getAnchor());
1002         }
1003         
1004         // Set document related parameter
1005         String docWebScope=(String)request.getAttribute(KNSConstants.DOCUMENT_WEB_SCOPE);
1006         if (docWebScope != null && docWebScope.trim().length() >= 0) {
1007             request.setAttribute(KNSConstants.DOCUMENT_WEB_SCOPE, docWebScope);
1008         }
1009 
1010         request.setAttribute(KNSConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObject(form));
1011         
1012         ActionForward forward=mapping.findForward(FORWARD_TEXT_AREA_UPDATE);
1013 
1014         if (LOG.isTraceEnabled()) {
1015             String lm=String.format("EXIT %s", (forward==null)?"null":forward.getPath());
1016             LOG.trace(lm);
1017         }
1018                         
1019         return forward;
1020     }
1021     
1022     /**
1023      * This method takes the {@link KNSConstants.METHOD_TO_CALL_ATTRIBUTE} out of the request
1024      * and parses it returning the required fields needed for a text area. The fields returned
1025      * are the following in this order.
1026      * <ol>
1027      * <li>{@link #TEXT_AREA_FIELD_NAME}</li>
1028      * <li>{@link #FORM_ACTION}</li>
1029      * <li>{@link #TEXT_AREA_FIELD_LABEL}</li>
1030      * <li>{@link #TEXT_AREA_READ_ONLY}</li>
1031      * <li>{@link #TEXT_AREA_MAX_LENGTH}</li>
1032      * </ol>
1033      * 
1034      * @param request the request to retrieve the textarea parameters
1035      * @return a string array holding the parsed fields
1036      */
1037     private String[] getTextAreaParams(HttpServletRequest request) {
1038         // parse out the important strings from our methodToCall parameter
1039         String fullParameter = (String) request.getAttribute(
1040                 KNSConstants.METHOD_TO_CALL_ATTRIBUTE);
1041 
1042         // parse textfieldname:htmlformaction
1043         String parameterFields = StringUtils.substringBetween(fullParameter,
1044                 KNSConstants.METHOD_TO_CALL_PARM2_LEFT_DEL,
1045                 KNSConstants.METHOD_TO_CALL_PARM2_RIGHT_DEL);
1046         if ( LOG.isDebugEnabled() ) {
1047             LOG.debug( "fullParameter: " + fullParameter );
1048             LOG.debug( "parameterFields: " + parameterFields );
1049         }
1050         String[] keyValue = null;
1051         if (StringUtils.isNotBlank(parameterFields)) {
1052             String[] textAreaParams = parameterFields.split(
1053                     KNSConstants.FIELD_CONVERSIONS_SEPARATOR);
1054             if ( LOG.isDebugEnabled() ) {
1055                 LOG.debug( "lookupParams: " + textAreaParams );
1056             }
1057             for (final String textAreaParam : textAreaParams) {
1058                 keyValue = textAreaParam.split(KNSConstants.FIELD_CONVERSION_PAIR_SEPARATOR);
1059 
1060                 if ( LOG.isDebugEnabled() ) {
1061                     LOG.debug( "keyValue[0]: " + keyValue[0] );
1062                     LOG.debug( "keyValue[1]: " + keyValue[1] );
1063                     LOG.debug( "keyValue[2]: " + keyValue[2] );
1064                     LOG.debug( "keyValue[3]: " + keyValue[3] );
1065                     LOG.debug( "keyValue[4]: " + keyValue[4] );
1066                 }
1067             }
1068         }
1069         
1070         return keyValue;
1071     }
1072     
1073     /**
1074      * This method is invoked from the TextArea.jsp for posting its value to the parent
1075      * page that called the extended text area page. The invocation is done through
1076      * Struts action. The default forwarding id is RiceContants.MAPPING_BASIC. This
1077      * can be overridden using the parameter key FORWARD_NEXT.
1078      * 
1079      * @param mapping
1080      * @param form
1081      * @param request
1082      * @param response
1083      * @return
1084      */
1085     public ActionForward postTextAreaToParent(ActionMapping mapping,
1086             ActionForm form,
1087             HttpServletRequest request,
1088             HttpServletResponse response) {
1089         
1090         if (LOG.isTraceEnabled()) {
1091             String lm=String.format("ENTRY %s%n%s", form.getClass().getSimpleName(),
1092                     request.getRequestURI());
1093             LOG.trace(lm);
1094         }
1095                         
1096         String forwardingId=request.getParameter(FORWARD_NEXT);
1097         if (forwardingId == null) {
1098             forwardingId=RiceConstants.MAPPING_BASIC;
1099         }
1100         ActionForward forward=mapping.findForward(forwardingId);
1101              
1102         if (LOG.isTraceEnabled()) {
1103             String lm=String.format("EXIT %s", (forward==null)?"null":forward.getPath());
1104             LOG.trace(lm);
1105         }
1106                         
1107         return forward;
1108     }
1109     
1110     /**
1111      * Use to add a methodToCall to the a list which will not have authorization checks.
1112      * This assumes that the call will be redirected (as in the case of a lookup) that will perform
1113      * the authorization.
1114      */
1115     protected final void addMethodToCallToUncheckedList( String methodToCall ) {
1116     	methodToCallsToNotCheckAuthorization.add(methodToCall);
1117     }
1118     
1119     /**
1120      * This method does all special processing on a document that should happen on each HTTP post (ie, save, route, approve, etc).
1121      */
1122     protected void doProcessingAfterPost( KualiForm form, HttpServletRequest request ) {
1123     	
1124     }
1125     
1126     protected BusinessObjectAuthorizationService getBusinessObjectAuthorizationService() {
1127     	if (businessObjectAuthorizationService == null) {
1128     		businessObjectAuthorizationService = KNSServiceLocator.getBusinessObjectAuthorizationService();
1129     	}
1130     	return businessObjectAuthorizationService;
1131     }
1132     
1133     protected EncryptionService getEncryptionService() {
1134     	if (encryptionService == null) {
1135     		encryptionService = KNSServiceLocator.getEncryptionService();
1136     	}
1137     	return encryptionService;
1138     }
1139 
1140 	public static String getApplicationBaseUrl() {
1141 		if ( applicationBaseUrl == null ) {
1142 			applicationBaseUrl = KNSServiceLocator.getKualiConfigurationService().getPropertyString(KNSConstants.APPLICATION_URL_KEY);
1143 		}
1144 		return applicationBaseUrl;
1145 	}
1146 	
1147 	/**
1148 	 * Returns the Base URL for the application server.
1149 	 * 
1150 	 * @deprecated Use getApplicationBaseUrl() instead.
1151 	 */
1152 	@Deprecated
1153 	protected String getBasePath( HttpServletRequest request ) {
1154 		return getApplicationBaseUrl();
1155 	}
1156 }