View Javadoc

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