View Javadoc

1   /**
2    * Copyright 2005-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  package org.kuali.rice.kns.web.struts.action;
17  
18  import java.util.Arrays;
19  import java.util.Enumeration;
20  import java.util.HashMap;
21  import java.util.HashSet;
22  import java.util.Map;
23  import java.util.Properties;
24  import java.util.Set;
25  
26  import javax.servlet.http.HttpServletRequest;
27  import javax.servlet.http.HttpServletResponse;
28  
29  import org.apache.commons.lang.ObjectUtils;
30  import org.apache.commons.lang.StringUtils;
31  import org.apache.struts.action.ActionForm;
32  import org.apache.struts.action.ActionForward;
33  import org.apache.struts.action.ActionMapping;
34  import org.apache.struts.actions.DispatchAction;
35  import org.kuali.rice.core.api.CoreApiServiceLocator;
36  import org.kuali.rice.core.api.encryption.EncryptionService;
37  import org.kuali.rice.core.api.util.RiceConstants;
38  import org.kuali.rice.core.framework.parameter.ParameterService;
39  import org.kuali.rice.core.framework.services.CoreFrameworkServiceLocator;
40  import org.kuali.rice.kim.api.KimConstants;
41  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
42  import org.kuali.rice.kns.lookup.LookupUtils;
43  import org.kuali.rice.kns.service.BusinessObjectAuthorizationService;
44  import org.kuali.rice.kns.service.KNSServiceLocator;
45  import org.kuali.rice.kns.util.KNSGlobalVariables;
46  import org.kuali.rice.kns.util.WebUtils;
47  import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
48  import org.kuali.rice.kns.web.struts.form.KualiForm;
49  import org.kuali.rice.kns.web.struts.form.LookupForm;
50  import org.kuali.rice.kns.web.struts.form.pojo.PojoForm;
51  import org.kuali.rice.kns.web.struts.form.pojo.PojoFormBase;
52  import org.kuali.rice.krad.bo.BusinessObject;
53  import org.kuali.rice.krad.document.authorization.DocumentAuthorizerBase;
54  import org.kuali.rice.krad.exception.AuthorizationException;
55  import org.kuali.rice.krad.service.KRADServiceLocator;
56  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
57  import org.kuali.rice.krad.service.KualiModuleService;
58  import org.kuali.rice.krad.service.ModuleService;
59  import org.kuali.rice.krad.util.GlobalVariables;
60  import org.kuali.rice.krad.util.KRADConstants;
61  import org.kuali.rice.krad.util.KRADUtils;
62  import org.kuali.rice.krad.util.UrlFactory;
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             selectedLine = Integer.parseInt(lineNumber);
300         }
301 
302         return selectedLine;
303     }
304 
305     /**
306      * Determines which tab was requested to be toggled
307      *
308      * @param request
309      * @return
310      */
311     protected String getTabToToggle(HttpServletRequest request) {
312         String tabToToggle = "";
313         String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
314         if (StringUtils.isNotBlank(parameterName)) {
315             tabToToggle = StringUtils.substringBetween(parameterName, ".tab", ".");
316         }
317 
318         return tabToToggle;
319     }
320 
321     /**
322      * Retrieves the header tab to navigate to.
323      *
324      * @param request
325      * @return
326      */
327     protected String getHeaderTabNavigateTo(HttpServletRequest request) {
328         String headerTabNavigateTo = RiceConstants.MAPPING_BASIC;
329         String imageContext = getImageContext(request, KRADConstants.NAVIGATE_TO);
330         if (StringUtils.isNotBlank(imageContext)) {
331             headerTabNavigateTo = imageContext;
332         }
333         return headerTabNavigateTo;
334     }
335 
336     /**
337      * Retrieves the header tab dispatch.
338      *
339      * @param request
340      * @return
341      */
342     protected String getHeaderTabDispatch(HttpServletRequest request) {
343         String headerTabDispatch = null;
344         String imageContext = getImageContext(request, KRADConstants.HEADER_DISPATCH);
345         if (StringUtils.isNotBlank(imageContext)) {
346             headerTabDispatch = imageContext;
347         }
348         else {
349             // In some cases it might be in request params instead
350             headerTabDispatch = request.getParameter(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
351         }
352         return headerTabDispatch;
353     }
354 
355     /**
356      * Retrieves the image context
357      *
358      * @param request
359      * @param contextKey
360      * @return
361      */
362     protected String getImageContext(HttpServletRequest request, String contextKey) {
363         String imageContext = "";
364         String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
365         if (StringUtils.isBlank(parameterName)) {
366             parameterName = request.getParameter("methodToCallPath");
367         }
368         if (StringUtils.isNotBlank(parameterName)) {
369             imageContext = StringUtils.substringBetween(parameterName, contextKey, ".");
370         }
371         return imageContext;
372     }
373 
374     protected String getReturnLocation(HttpServletRequest request, ActionMapping mapping) {
375     	String mappingPath = mapping.getPath();
376     	String basePath = getApplicationBaseUrl();
377         return basePath + ("/lookup".equals(mappingPath) || "/maintenance".equals(mappingPath) || "/multipleValueLookup".equals(mappingPath) ? "/kr" : "") + mappingPath + ".do";
378     }
379 
380     /**
381      * 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
382      * 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
383      * the property.
384      * 
385      * @param boClass a class implementing boClass, representing the BO that will be looked up
386      * @param parameterName the name of the parameter
387      * @param parameterValuePropertyName the property (relative to the form object) where the value to be passed into the lookup/inquiry may be found
388      * @param form
389      * @param request
390      * @return
391      */
392     protected String retrieveLookupParameterValue(Class<? extends BusinessObject> boClass, String parameterName, String parameterValuePropertyName, ActionForm form, HttpServletRequest request) throws Exception {
393     	String value;
394     	if (StringUtils.contains(parameterValuePropertyName, "'")) {
395     		value = StringUtils.replace(parameterValuePropertyName, "'", "");
396     	}
397     	else if (request.getParameterMap().containsKey(parameterValuePropertyName)) {
398     		value = request.getParameter(parameterValuePropertyName);
399     	}
400     	else {
401     		if (form instanceof KualiForm) {
402     			value = ((KualiForm) form).retrieveFormValueForLookupInquiryParameters(parameterName, parameterValuePropertyName);
403     		} else {
404 	    		if (LOG.isDebugEnabled()) {
405 	    			LOG.debug("Unable to retrieve lookup/inquiry parameter value for parameter name " + parameterName + " parameter value property " + parameterValuePropertyName);
406 	    		}
407 	    		value = null;
408     		}
409     	}
410     	
411     	if (value != null && boClass != null && getBusinessObjectAuthorizationService().attributeValueNeedsToBeEncryptedOnFormsAndLinks(boClass, parameterName)) {
412     		value = getEncryptionService().encrypt(value) + EncryptionService.ENCRYPTION_POST_PREFIX;
413     	}
414     	return value;
415     }
416     
417     /**
418      * Takes care of storing the action form in the User session and forwarding to the lookup action.
419      *
420      * @param mapping
421      * @param form
422      * @param request
423      * @param response
424      * @return
425      * @throws Exception
426      */
427     @SuppressWarnings("unchecked")
428 	public ActionForward performLookup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
429         // parse out the important strings from our methodToCall parameter
430         String fullParameter = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
431         validateLookupInquiryFullParameter(request, form, fullParameter);
432         
433         KualiForm kualiForm = (KualiForm) form;
434         
435         // when we return from the lookup, our next request's method to call is going to be refresh
436         kualiForm.registerEditableProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER);
437         
438         // parse out the baseLookupUrl if there is one
439         String baseLookupUrl = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM14_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM14_RIGHT_DEL);
440         
441         // parse out business object class name for lookup
442         String boClassName = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL, KRADConstants.METHOD_TO_CALL_BOPARM_RIGHT_DEL);
443         if (StringUtils.isBlank(boClassName)) {
444             throw new RuntimeException("Illegal call to perform lookup, no business object class name specified.");
445         }
446         Class boClass = null;
447 		try{
448 			boClass = Class.forName(boClassName);
449 		} catch(ClassNotFoundException cnfex){
450             throw new IllegalArgumentException("The classname (" + boClassName + ") does not represent a valid class which this application understands.");
451 		}
452 		
453         // build the parameters for the lookup url
454         Properties parameters = new Properties();
455         String conversionFields = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL);
456         if (StringUtils.isNotBlank(conversionFields)) {
457             parameters.put(KRADConstants.CONVERSION_FIELDS_PARAMETER, conversionFields);
458             
459             // register each of the destination parameters of the field conversion string as editable
460             String[] fieldConversions = conversionFields.split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
461             for (String fieldConversion : fieldConversions) {
462             	String destination = fieldConversion.split(KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR)[1];
463             	kualiForm.registerEditableProperty(destination);
464             }
465         }
466 
467         // pass values from form that should be pre-populated on lookup search
468         String parameterFields = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM2_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM2_RIGHT_DEL);
469         if ( LOG.isDebugEnabled() ) {
470             LOG.debug( "fullParameter: " + fullParameter );
471             LOG.debug( "parameterFields: " + parameterFields );
472         }
473         if (StringUtils.isNotBlank(parameterFields)) {
474             String[] lookupParams = parameterFields.split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
475             if ( LOG.isDebugEnabled() ) {
476             	 LOG.debug( "lookupParams: " + Arrays.toString(lookupParams) ); 
477             }
478             for (String lookupParam : lookupParams) {
479             	String[] keyValue = lookupParam.split(KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR);
480                 if (keyValue.length != 2) {
481 					throw new RuntimeException("malformed field conversion pair: " + Arrays.toString(keyValue));
482 				} 
483 
484                 String lookupParameterValue = retrieveLookupParameterValue(boClass, keyValue[1], keyValue[0], form, request);
485                 if (StringUtils.isNotBlank(lookupParameterValue)) {
486                 	parameters.put(keyValue[1], lookupParameterValue);
487                 }
488 
489                 if ( LOG.isDebugEnabled() ) {
490                     LOG.debug( "keyValue[0]: " + keyValue[0] );
491                     LOG.debug( "keyValue[1]: " + keyValue[1] );
492                 }
493             }
494         }
495 
496         // pass values from form that should be read-Only on lookup search
497         String readOnlyFields = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM8_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM8_RIGHT_DEL);
498         if (StringUtils.isNotBlank(readOnlyFields)) {
499             parameters.put(KRADConstants.LOOKUP_READ_ONLY_FIELDS, readOnlyFields);
500         }
501 
502         if ( LOG.isDebugEnabled() ) {
503             LOG.debug( "fullParameter: " + fullParameter );
504             LOG.debug( "readOnlyFields: " + readOnlyFields );
505         }
506 
507         // grab whether or not the "return value" link should be hidden or not
508         String hideReturnLink = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM3_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM3_RIGHT_DEL);
509         if (StringUtils.isNotBlank(hideReturnLink)) {
510             parameters.put(KRADConstants.HIDE_LOOKUP_RETURN_LINK, hideReturnLink);
511         }
512 
513         // add the optional extra button source and parameters string
514         String extraButtonSource = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM4_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM4_RIGHT_DEL);
515         if (StringUtils.isNotBlank(extraButtonSource)) {
516             parameters.put(KRADConstants.EXTRA_BUTTON_SOURCE, extraButtonSource);
517         }
518         String extraButtonParams = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM5_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM5_RIGHT_DEL);
519         if (StringUtils.isNotBlank(extraButtonParams)) {
520             parameters.put(KRADConstants.EXTRA_BUTTON_PARAMS, extraButtonParams);
521         }
522 
523         String lookupAction = KRADConstants.LOOKUP_ACTION;
524 
525         // is this a multi-value return?
526         boolean isMultipleValue = false;
527         String multipleValues = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM6_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM6_RIGHT_DEL);
528         if ((new Boolean(multipleValues).booleanValue())) {
529             parameters.put(KRADConstants.MULTIPLE_VALUE, multipleValues);
530             lookupAction = KRADConstants.MULTIPLE_VALUE_LOOKUP_ACTION;
531             isMultipleValue = true;
532         }
533 
534         // the name of the collection being looked up (primarily for multivalue lookups
535         String lookedUpCollectionName = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM11_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM11_RIGHT_DEL);
536         if (StringUtils.isNotBlank(lookedUpCollectionName)) {
537             parameters.put(KRADConstants.LOOKED_UP_COLLECTION_NAME, lookedUpCollectionName);
538         }
539 
540         // grab whether or not the "supress actions" column should be hidden or not
541         String supressActions = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM7_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM7_RIGHT_DEL);
542         if (StringUtils.isNotBlank(supressActions)) {
543             parameters.put(KRADConstants.SUPPRESS_ACTIONS, supressActions);
544         }
545 
546         // grab the references that should be refreshed upon returning from the lookup
547         String referencesToRefresh = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM10_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM10_RIGHT_DEL);
548         if (StringUtils.isNotBlank(referencesToRefresh)) {
549             parameters.put(KRADConstants.REFERENCES_TO_REFRESH, referencesToRefresh);
550         }
551 
552         // anchor, if it exists
553         if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
554             parameters.put(KRADConstants.LOOKUP_ANCHOR, ((KualiForm) form).getAnchor());
555         }
556 
557         // now add required parameters
558         parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, "start");
559 
560         // pass value from form that shows if autoSearch is desired for lookup search
561         String autoSearch = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM9_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM9_RIGHT_DEL);
562 
563         if (StringUtils.isNotBlank(autoSearch)) {
564             parameters.put(KRADConstants.LOOKUP_AUTO_SEARCH, autoSearch);
565             if ("YES".equalsIgnoreCase(autoSearch)){
566                 parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, "search");
567             }
568         }
569 
570         parameters.put(KRADConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObjectWithGeneratedKey(form));
571         parameters.put(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, boClassName);
572 
573         parameters.put(KRADConstants.RETURN_LOCATION_PARAMETER, getReturnLocation(request, mapping));
574         
575     	if (form instanceof KualiDocumentFormBase) {
576     		String docNum = ((KualiDocumentFormBase) form).getDocument().getDocumentNumber();
577 			if(docNum != null){
578 				parameters.put(KRADConstants.DOC_NUM, docNum);
579 			}
580 		}else if(form instanceof LookupForm){
581 			String docNum = ((LookupForm) form).getDocNum();
582 			if(docNum != null){
583 				parameters.put(KRADConstants.DOC_NUM, ((LookupForm) form).getDocNum());
584 			}
585     	}
586 
587     	if (boClass != null) {
588     		ModuleService responsibleModuleService = getKualiModuleService().getResponsibleModuleService(boClass);
589     		if(responsibleModuleService!=null && responsibleModuleService.isExternalizable(boClass)){
590     			Map<String, String> parameterMap = new HashMap<String, String>();
591     			Enumeration<Object> e = parameters.keys();
592     			while (e.hasMoreElements()) {
593     				String paramName = (String) e.nextElement();
594     				parameterMap.put(paramName, parameters.getProperty(paramName));
595     			}
596     			return new ActionForward(responsibleModuleService.getExternalizableBusinessObjectLookupUrl(boClass, parameterMap), true);
597     		}
598     	}
599 		
600     	if (StringUtils.isBlank(baseLookupUrl)) {
601     		baseLookupUrl = getApplicationBaseUrl() + "/kr/" + lookupAction;
602     	} else {
603     		if (isMultipleValue) {
604     			LookupUtils.transformLookupUrlToMultiple(baseLookupUrl);
605     		}
606     	}
607     	String lookupUrl = UrlFactory.parameterizeUrl(baseLookupUrl, parameters);
608         return new ActionForward(lookupUrl, true);
609     }
610 
611     protected void validateLookupInquiryFullParameter(HttpServletRequest request, ActionForm form, String fullParameter){
612     	PojoFormBase pojoFormBase = (PojoFormBase) form;
613         if(WebUtils.isFormSessionDocument((PojoFormBase) form)){
614         	if(!pojoFormBase.isPropertyEditable(fullParameter)) {
615         		throw new RuntimeException("The methodToCallAttribute is not registered as an editable property.");
616         	}
617         }
618     }
619     
620     @SuppressWarnings("unchecked")
621 	public ActionForward performInquiry(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
622         // parse out the important strings from our methodToCall parameter
623         String fullParameter = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
624         validateLookupInquiryFullParameter(request, form, fullParameter);
625         
626         // when javascript is disabled, the inquiry will appear in the same window as the document.  when we close the inquiry, 
627         // our next request's method to call is going to be refresh
628         KualiForm kualiForm = (KualiForm) form;
629         kualiForm.registerEditableProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER);
630         
631         // parse out business object class name for lookup
632         String boClassName = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL, KRADConstants.METHOD_TO_CALL_BOPARM_RIGHT_DEL);
633         if (StringUtils.isBlank(boClassName)) {
634             throw new RuntimeException("Illegal call to perform inquiry, no business object class name specified.");
635         }
636 
637         // build the parameters for the inquiry url
638         Properties parameters = new Properties();
639         parameters.put(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, boClassName);
640 
641         parameters.put(KRADConstants.RETURN_LOCATION_PARAMETER, getReturnLocation(request, mapping));
642 
643         // pass values from form that should be pre-populated on inquiry
644         String parameterFields = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM2_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM2_RIGHT_DEL);
645         if ( LOG.isDebugEnabled() ) {
646             LOG.debug( "fullParameter: " + fullParameter );
647             LOG.debug( "parameterFields: " + parameterFields );
648         }
649         if (StringUtils.isNotBlank(parameterFields)) {
650             // TODO : create a method for this to be used by both lookup & inquiry ?
651             String[] inquiryParams = parameterFields.split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
652             if ( LOG.isDebugEnabled() ) {
653                 LOG.debug( "inquiryParams: " + inquiryParams );
654             }
655             Class<? extends BusinessObject> boClass = (Class<? extends BusinessObject>) Class.forName(boClassName);
656             for (String inquiryParam : inquiryParams) {
657                 String[] keyValue = inquiryParam.split(KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR);
658 
659                 String inquiryParameterValue = retrieveLookupParameterValue(boClass, keyValue[1], keyValue[0], form, request);
660                 if (inquiryParameterValue == null) {
661                 	parameters.put(keyValue[1], "directInquiryKeyNotSpecified");
662                 }
663                 else {
664                 	parameters.put(keyValue[1], inquiryParameterValue);
665                 }
666 
667                 if ( LOG.isDebugEnabled() ) {
668                     LOG.debug( "keyValue[0]: " + keyValue[0] );
669                     LOG.debug( "keyValue[1]: " + keyValue[1] );
670                 }
671             }
672         }
673         parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, "start");
674         parameters.put(KRADConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObjectWithGeneratedKey(form));
675         String inquiryUrl = null;
676         try {
677         	Class.forName(boClassName);
678         	inquiryUrl = getApplicationBaseUrl() + "/kr/" + KRADConstants.DIRECT_INQUIRY_ACTION;
679         } catch ( ClassNotFoundException ex ) {
680             // allow inquiry url to be null (and therefore no inquiry link will be displayed) but at least log a warning
681         	LOG.warn("Class name does not represent a valid class which this application understands: " + boClassName);
682         }
683         inquiryUrl = UrlFactory.parameterizeUrl(inquiryUrl, parameters);
684         return new ActionForward(inquiryUrl, true);
685 
686     }
687 
688     /**
689      * This method handles rendering the question component, but without any of the extra error fields
690      *
691      * @param mapping
692      * @param form
693      * @param request
694      * @param response
695      * @param questionId
696      * @param questionText
697      * @param questionType
698      * @param caller
699      * @param context
700      * @return ActionForward
701      * @throws Exception
702      */
703     protected ActionForward performQuestionWithoutInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionId, String questionText, String questionType, String caller, String context) throws Exception {
704         return performQuestion(mapping, form, request, response, questionId, questionText, questionType, caller, context, false, "", "", "", "");
705     }
706 
707     /**
708      * Handles rendering a question prompt - without a specified context.
709      *
710      * @param mapping
711      * @param form
712      * @param request
713      * @param response
714      * @param questionId
715      * @param questionText
716      * @param questionType
717      * @param caller
718      * @param context
719      * @return ActionForward
720      * @throws Exception
721      */
722     protected ActionForward performQuestionWithInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionId, String questionText, String questionType, String caller, String context) throws Exception {
723         return performQuestion(mapping, form, request, response, questionId, questionText, questionType, caller, context, true, "", "", "", "");
724     }
725 
726     /**
727      * Handles re-rendering a question prompt because of an error on what was submitted.
728      *
729      * @param mapping
730      * @param form
731      * @param request
732      * @param response
733      * @param questionId
734      * @param questionText
735      * @param questionType
736      * @param caller
737      * @param context
738      * @param reason
739      * @param errorKey
740      * @param errorPropertyName
741      * @param errorParameter
742      * @return ActionForward
743      * @throws Exception
744      */
745     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 {
746         return performQuestion(mapping, form, request, response, questionId, questionText, questionType, caller, context, true, reason, errorKey, errorPropertyName, errorParameter);
747     }
748 
749     /**
750      * Handles rendering a question prompt - with a specified context.
751      *
752      * @param mapping
753      * @param form
754      * @param request
755      * @param response
756      * @param questionId
757      * @param questionText
758      * @param questionType
759      * @param caller
760      * @param context
761      * @param showReasonField
762      * @param reason
763      * @param errorKey
764      * @param errorPropertyName
765      * @param errorParameter
766      * @return ActionForward
767      * @throws Exception
768      */
769     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 {
770         Properties parameters = new Properties();
771 
772         parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, "start");
773         parameters.put(KRADConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObjectWithGeneratedKey(form));
774         parameters.put(KRADConstants.CALLING_METHOD, caller);
775         parameters.put(KRADConstants.QUESTION_INST_ATTRIBUTE_NAME, questionId);
776         parameters.put(KRADConstants.QUESTION_IMPL_ATTRIBUTE_NAME, questionType);
777         parameters.put(KRADConstants.QUESTION_TEXT_ATTRIBUTE_NAME, questionText);
778         parameters.put(KRADConstants.RETURN_LOCATION_PARAMETER, getReturnLocation(request, mapping));
779         parameters.put(KRADConstants.QUESTION_CONTEXT, context);
780         parameters.put(KRADConstants.QUESTION_SHOW_REASON_FIELD, Boolean.toString(showReasonField));
781         parameters.put(KRADConstants.QUESTION_REASON_ATTRIBUTE_NAME, reason);
782         parameters.put(KRADConstants.QUESTION_ERROR_KEY, errorKey);
783         parameters.put(KRADConstants.QUESTION_ERROR_PROPERTY_NAME, errorPropertyName);
784         parameters.put(KRADConstants.QUESTION_ERROR_PARAMETER, errorParameter);
785         parameters.put(KRADConstants.QUESTION_ANCHOR, form instanceof KualiForm ? ObjectUtils.toString(((KualiForm) form).getAnchor()) : "");
786         Object methodToCallAttribute = request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
787         if (methodToCallAttribute != null) {
788             parameters.put(KRADConstants.METHOD_TO_CALL_PATH, methodToCallAttribute);
789             ((PojoForm) form).registerEditableProperty(String.valueOf(methodToCallAttribute));
790         }
791         
792     	if (form instanceof KualiDocumentFormBase) {
793     		String docNum = ((KualiDocumentFormBase) form).getDocument().getDocumentNumber();
794     		if(docNum != null){
795     			parameters.put(KRADConstants.DOC_NUM, ((KualiDocumentFormBase) form)
796 					.getDocument().getDocumentNumber());
797     		}
798 		}
799 
800         String questionUrl = UrlFactory.parameterizeUrl(getApplicationBaseUrl() + "/kr/" + KRADConstants.QUESTION_ACTION, parameters);
801         return new ActionForward(questionUrl, true);
802     }
803 
804 
805     /**
806      * Takes care of storing the action form in the User session and forwarding to the workflow workgroup lookup action.
807      *
808      * @param mapping
809      * @param form
810      * @param request
811      * @param response
812      * @return
813      * @throws Exception
814      */
815     public ActionForward performWorkgroupLookup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
816     	String returnUrl = null;
817     	if ("/kr".equals(mapping.getModuleConfig().getPrefix())) {
818     		returnUrl = getApplicationBaseUrl() + mapping.getModuleConfig().getPrefix() + mapping.getPath() + ".do";
819     	} else {
820     		returnUrl = getApplicationBaseUrl() + mapping.getPath() + ".do";
821     	}
822 
823 
824         String fullParameter = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
825         String conversionFields = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL);
826 
827         String deploymentBaseUrl = KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
828                 KRADConstants.WORKFLOW_URL_KEY);
829         String workgroupLookupUrl = deploymentBaseUrl + "/Lookup.do?lookupableImplServiceName=WorkGroupLookupableImplService&methodToCall=start&docFormKey=" + GlobalVariables.getUserSession().addObjectWithGeneratedKey(form);
830 
831         if (conversionFields != null) {
832             workgroupLookupUrl += "&conversionFields=" + conversionFields;
833         }
834     	if (form instanceof KualiDocumentFormBase) {
835 			workgroupLookupUrl +="&docNum="+ ((KualiDocumentFormBase) form).getDocument().getDocumentNumber();
836 		}
837     	
838         workgroupLookupUrl += "&returnLocation=" + returnUrl;
839 
840         return new ActionForward(workgroupLookupUrl, true);
841     }
842 
843     /**
844      * Handles requests that originate via Header Tabs.
845      *
846      * @param mapping
847      * @param form
848      * @param request
849      * @param response
850      * @return
851      * @throws Exception
852      */
853     public ActionForward headerTab(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
854         // header tab actions can do two things - 1, call into an action and perform what needs to happen in there and 2, forward to
855         // a new location.
856         String headerTabDispatch = getHeaderTabDispatch(request);
857         if (StringUtils.isNotEmpty(headerTabDispatch)) {
858             ActionForward forward = dispatchMethod(mapping, form, request, response, headerTabDispatch);
859             if (GlobalVariables.getMessageMap().getNumberOfPropertiesWithErrors() > 0) {
860                 return mapping.findForward(RiceConstants.MAPPING_BASIC);
861             }
862             this.doTabOpenOrClose(mapping, form, request, response, false);
863             if (forward.getRedirect()) {
864                 return forward;
865             }
866         }
867         return dispatchMethod(mapping, form, request, response, getHeaderTabNavigateTo(request));
868     }
869 
870     /**
871      * Override this method to provide action-level access controls to the application.
872      *
873      * @param form
874      * @throws AuthorizationException
875      */
876     protected void checkAuthorization( ActionForm form, String methodToCall) throws AuthorizationException 
877     {
878     	String principalId = GlobalVariables.getUserSession().getPrincipalId();
879     	Map<String, String> roleQualifier = new HashMap<String, String>(getRoleQualification(form, methodToCall));
880     	Map<String, String> permissionDetails = KRADUtils.getNamespaceAndActionClass(this.getClass());
881     	
882         if (!KimApiServiceLocator.getPermissionService().isAuthorizedByTemplateName(principalId, KRADConstants.KRAD_NAMESPACE,
883         		KimConstants.PermissionTemplateNames.USE_SCREEN, permissionDetails, roleQualifier ))
884         {
885             throw new AuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(), 
886             		methodToCall,
887             		this.getClass().getSimpleName());
888         }
889     }
890     
891     /** 
892      * override this method to add data from the form for role qualification in the authorization check
893      */
894     protected Map<String,String> getRoleQualification(ActionForm form, String methodToCall) {
895     	return new HashMap<String,String>();
896     }
897 
898     protected static KualiModuleService getKualiModuleService() {
899         if ( kualiModuleService == null ) {
900             kualiModuleService = KRADServiceLocatorWeb.getKualiModuleService();
901         }
902         return kualiModuleService;
903     }
904 
905     /**
906      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
907      * <p>Value is textAreaFieldName
908      */
909     public static final String TEXT_AREA_FIELD_NAME="textAreaFieldName";
910     /**
911      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
912      * <p>Value is textAreaFieldLabel
913     */
914     public static final String TEXT_AREA_FIELD_LABEL="textAreaFieldLabel";
915     /**
916      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
917      * <p>Value is textAreaReadOnly
918     */
919     public static final String TEXT_AREA_READ_ONLY="textAreaReadOnly";
920     /**
921      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
922      * <p>Value is textAreaFieldAnchor
923     */
924     public static final String TEXT_AREA_FIELD_ANCHOR="textAreaFieldAnchor";
925     /**
926      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
927      * <p>Value is textAreaFieldAnchor
928     */
929     public static final String TEXT_AREA_MAX_LENGTH="textAreaMaxLength";
930     /**
931      * Constant defined to match with TextArea.jsp and updateTextArea function in core.js
932      * <p>Value is htmlFormAction
933     */
934     public static final String FORM_ACTION="htmlFormAction";
935     /**
936      * Constant defined to match input parameter from URL and from TextArea.jsp.
937      * <p>Value is methodToCall
938     */
939     public static final String METHOD_TO_CALL="methodToCall";
940     /**
941      * Constant defined to match with global forwarding in struts-config.xml
942      * for Text Area Update.
943      * <p>Value is updateTextArea
944     */
945     public static final String FORWARD_TEXT_AREA_UPDATE="updateTextArea";
946     /**
947      * Constant defined to match with method to call in TextArea.jsp.
948      * <p>Value is postTextAreaToParent
949     */
950     public static final String POST_TEXT_AREA_TO_PARENT="postTextAreaToParent";
951     /**
952      * Constant defined to match with local forwarding in struts-config.xml
953      * for the parent of the Updated Text Area.
954      * <p>Value is forwardNext
955     */
956     public static final String FORWARD_NEXT="forwardNext";
957     
958     /**
959      * Constant defined to match with method call in module-locked.jsp which is
960      * set to a message that is displayed when the module is locked.
961      */
962     public static final String MODULE_LOCKED_MESSAGE = "moduleLockedMessage";
963 
964     /**
965      * This method is invoked when Java Script is turned off from the web browser. It
966      * setup the information that the update text area requires for copying current text
967      * in the calling page text area and returning to the calling page. The information
968      * is passed to the JSP through Http Request attributes. All other parameters are
969      * forwarded 
970      *  
971      * @param mapping
972      * @param form
973      * @param request
974      * @param response
975      * @return
976      */
977     public ActionForward updateTextArea(ActionMapping mapping,
978             ActionForm form,
979             HttpServletRequest request,
980             HttpServletResponse response)  {
981         if (LOG.isTraceEnabled()) {
982             String lm=String.format("ENTRY %s%n%s", form.getClass().getSimpleName(),
983                     request.getRequestURI());
984             LOG.trace(lm);
985         }
986                                 
987         final String[] keyValue = getTextAreaParams(request);
988         
989         request.setAttribute(TEXT_AREA_FIELD_NAME, keyValue[0]);
990         request.setAttribute(FORM_ACTION,keyValue[1]);
991         request.setAttribute(TEXT_AREA_FIELD_LABEL,keyValue[2]);
992         request.setAttribute(TEXT_AREA_READ_ONLY,keyValue[3]);
993         request.setAttribute(TEXT_AREA_MAX_LENGTH,keyValue[4]);
994         if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
995             request.setAttribute(TEXT_AREA_FIELD_ANCHOR,((KualiForm) form).getAnchor());
996         }
997         
998         // Set document related parameter
999         String docWebScope=(String)request.getAttribute(KRADConstants.DOCUMENT_WEB_SCOPE);
1000         if (docWebScope != null && docWebScope.trim().length() >= 0) {
1001             request.setAttribute(KRADConstants.DOCUMENT_WEB_SCOPE, docWebScope);
1002         }
1003 
1004         request.setAttribute(KRADConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObjectWithGeneratedKey(form));
1005         
1006         ActionForward forward=mapping.findForward(FORWARD_TEXT_AREA_UPDATE);
1007 
1008         if (LOG.isTraceEnabled()) {
1009             String lm=String.format("EXIT %s", (forward==null)?"null":forward.getPath());
1010             LOG.trace(lm);
1011         }
1012                         
1013         return forward;
1014     }
1015     
1016     /**
1017      * This method takes the {@link org.kuali.rice.krad.util.KRADConstants.METHOD_TO_CALL_ATTRIBUTE} out of the request
1018      * and parses it returning the required fields needed for a text area. The fields returned
1019      * are the following in this order.
1020      * <ol>
1021      * <li>{@link #TEXT_AREA_FIELD_NAME}</li>
1022      * <li>{@link #FORM_ACTION}</li>
1023      * <li>{@link #TEXT_AREA_FIELD_LABEL}</li>
1024      * <li>{@link #TEXT_AREA_READ_ONLY}</li>
1025      * <li>{@link #TEXT_AREA_MAX_LENGTH}</li>
1026      * </ol>
1027      * 
1028      * @param request the request to retrieve the textarea parameters
1029      * @return a string array holding the parsed fields
1030      */
1031     private String[] getTextAreaParams(HttpServletRequest request) {
1032         // parse out the important strings from our methodToCall parameter
1033         String fullParameter = (String) request.getAttribute(
1034                 KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
1035 
1036         // parse textfieldname:htmlformaction
1037         String parameterFields = StringUtils.substringBetween(fullParameter,
1038                 KRADConstants.METHOD_TO_CALL_PARM2_LEFT_DEL,
1039                 KRADConstants.METHOD_TO_CALL_PARM2_RIGHT_DEL);
1040         if ( LOG.isDebugEnabled() ) {
1041             LOG.debug( "fullParameter: " + fullParameter );
1042             LOG.debug( "parameterFields: " + parameterFields );
1043         }
1044         String[] keyValue = null;
1045         if (StringUtils.isNotBlank(parameterFields)) {
1046             String[] textAreaParams = parameterFields.split(
1047                     KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
1048             if ( LOG.isDebugEnabled() ) {
1049                 LOG.debug( "lookupParams: " + textAreaParams );
1050             }
1051             for (final String textAreaParam : textAreaParams) {
1052                 keyValue = textAreaParam.split(KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR);
1053 
1054                 if ( LOG.isDebugEnabled() ) {
1055                     LOG.debug( "keyValue[0]: " + keyValue[0] );
1056                     LOG.debug( "keyValue[1]: " + keyValue[1] );
1057                     LOG.debug( "keyValue[2]: " + keyValue[2] );
1058                     LOG.debug( "keyValue[3]: " + keyValue[3] );
1059                     LOG.debug( "keyValue[4]: " + keyValue[4] );
1060                 }
1061             }
1062         }
1063         
1064         return keyValue;
1065     }
1066     
1067     /**
1068      * This method is invoked from the TextArea.jsp for posting its value to the parent
1069      * page that called the extended text area page. The invocation is done through
1070      * Struts action. The default forwarding id is RiceContants.MAPPING_BASIC. This
1071      * can be overridden using the parameter key FORWARD_NEXT.
1072      * 
1073      * @param mapping
1074      * @param form
1075      * @param request
1076      * @param response
1077      * @return
1078      */
1079     public ActionForward postTextAreaToParent(ActionMapping mapping,
1080             ActionForm form,
1081             HttpServletRequest request,
1082             HttpServletResponse response) {
1083         
1084         if (LOG.isTraceEnabled()) {
1085             String lm=String.format("ENTRY %s%n%s", form.getClass().getSimpleName(),
1086                     request.getRequestURI());
1087             LOG.trace(lm);
1088         }
1089                         
1090         String forwardingId=request.getParameter(FORWARD_NEXT);
1091         if (forwardingId == null) {
1092             forwardingId=RiceConstants.MAPPING_BASIC;
1093         }
1094         ActionForward forward=mapping.findForward(forwardingId);
1095              
1096         if (LOG.isTraceEnabled()) {
1097             String lm=String.format("EXIT %s", (forward==null)?"null":forward.getPath());
1098             LOG.trace(lm);
1099         }
1100                         
1101         return forward;
1102     }
1103     
1104     /**
1105      * Use to add a methodToCall to the a list which will not have authorization checks.
1106      * This assumes that the call will be redirected (as in the case of a lookup) that will perform
1107      * the authorization.
1108      */
1109     protected final void addMethodToCallToUncheckedList( String methodToCall ) {
1110     	methodToCallsToNotCheckAuthorization.add(methodToCall);
1111     }
1112     
1113     /**
1114      * This method does all special processing on a document that should happen on each HTTP post (ie, save, route, approve, etc).
1115      */
1116     protected void doProcessingAfterPost( KualiForm form, HttpServletRequest request ) {
1117     	
1118     }
1119     
1120     protected BusinessObjectAuthorizationService getBusinessObjectAuthorizationService() {
1121     	if (businessObjectAuthorizationService == null) {
1122     		businessObjectAuthorizationService = KNSServiceLocator.getBusinessObjectAuthorizationService();
1123     	}
1124     	return businessObjectAuthorizationService;
1125     }
1126     
1127     protected EncryptionService getEncryptionService() {
1128     	if (encryptionService == null) {
1129     		encryptionService = CoreApiServiceLocator.getEncryptionService();
1130     	}
1131     	return encryptionService;
1132     }
1133 
1134 	public static String getApplicationBaseUrl() {
1135 		if ( applicationBaseUrl == null ) {
1136 			applicationBaseUrl = KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
1137                     KRADConstants.APPLICATION_URL_KEY);
1138 		}
1139 		return applicationBaseUrl;
1140 	}
1141 	
1142 	protected boolean isModuleLocked(ActionForm form, String methodToCall, HttpServletRequest request) {
1143 		String boClass = request.getParameter(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE);
1144 		ModuleService moduleService = null;
1145 		if(StringUtils.isNotBlank(boClass)) {
1146 			try {
1147 				moduleService = getKualiModuleService().getResponsibleModuleService(Class.forName(boClass));
1148 			} catch (ClassNotFoundException classNotFoundException) {
1149 				LOG.warn("BO class not found: " + boClass, classNotFoundException);
1150 			}
1151 		} else {
1152 			moduleService = getKualiModuleService().getResponsibleModuleService(this.getClass());
1153 		}
1154 		if(moduleService != null && moduleService.isLocked()) {
1155 			String principalId = GlobalVariables.getUserSession().getPrincipalId();
1156 			String namespaceCode = KRADConstants.KUALI_RICE_SYSTEM_NAMESPACE;
1157 			String permissionName = KimConstants.PermissionNames.ACCESS_LOCKED_MODULE;
1158 			Map<String, String> permissionDetails = new HashMap<String, String>();
1159 			Map<String, String> qualification = getRoleQualification(form, methodToCall);
1160 			if(!KimApiServiceLocator.getPermissionService().isAuthorized(principalId, namespaceCode, permissionName, permissionDetails, qualification)) {
1161 				ParameterService parameterSerivce = CoreFrameworkServiceLocator.getParameterService();
1162 				String messageParamNamespaceCode = moduleService.getModuleConfiguration().getNamespaceCode();
1163 				String messageParamComponentCode = KRADConstants.DetailTypes.OLTP_LOCKOUT_DETAIL_TYPE;
1164 				String messageParamName = KRADConstants.SystemGroupParameterNames.OLTP_LOCKOUT_MESSAGE_PARM;
1165 				String lockoutMessage = parameterSerivce.getParameterValueAsString(messageParamNamespaceCode, messageParamComponentCode, messageParamName);
1166 				
1167 				if(StringUtils.isBlank(lockoutMessage)) {
1168 					String defaultMessageParamName = KRADConstants.SystemGroupParameterNames.OLTP_LOCKOUT_DEFAULT_MESSAGE;
1169 					lockoutMessage = parameterSerivce.getParameterValueAsString(KRADConstants.KRAD_NAMESPACE, messageParamComponentCode, defaultMessageParamName);
1170 				}
1171 				request.setAttribute(MODULE_LOCKED_MESSAGE, lockoutMessage);
1172 				return true;
1173 			}
1174 		}
1175 		if(moduleService != null) {
1176 			System.out.println("BENNETT: module = " + moduleService.getModuleConfiguration().getNamespaceCode());
1177 		}
1178 		return false;
1179 	}
1180 }