View Javadoc

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