View Javadoc

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