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