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