View Javadoc

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