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