View Javadoc

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