001    /**
002     * Copyright 2005-2012 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    package org.kuali.rice.kns.web.struts.action;
017    
018    import org.apache.commons.lang.ObjectUtils;
019    import org.apache.commons.lang.StringUtils;
020    import org.apache.struts.action.ActionForm;
021    import org.apache.struts.action.ActionForward;
022    import org.apache.struts.action.ActionMapping;
023    import org.apache.struts.actions.DispatchAction;
024    import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
025    import org.kuali.rice.coreservice.framework.parameter.ParameterService;
026    import org.kuali.rice.core.api.CoreApiServiceLocator;
027    import org.kuali.rice.core.api.encryption.EncryptionService;
028    import org.kuali.rice.core.api.util.RiceConstants;
029    import org.kuali.rice.kew.api.KewApiConstants;
030    import org.kuali.rice.kim.api.KimConstants;
031    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
032    import org.kuali.rice.kns.document.authorization.DocumentAuthorizerBase;
033    import org.kuali.rice.kns.lookup.LookupUtils;
034    import org.kuali.rice.kns.service.BusinessObjectAuthorizationService;
035    import org.kuali.rice.kns.service.KNSServiceLocator;
036    import org.kuali.rice.kns.util.KNSGlobalVariables;
037    import org.kuali.rice.kns.util.WebUtils;
038    import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
039    import org.kuali.rice.kns.web.struts.form.KualiForm;
040    import org.kuali.rice.kns.web.struts.form.LookupForm;
041    import org.kuali.rice.kns.web.struts.form.pojo.PojoForm;
042    import org.kuali.rice.kns.web.struts.form.pojo.PojoFormBase;
043    import org.kuali.rice.krad.bo.BusinessObject;
044    import org.kuali.rice.krad.exception.AuthorizationException;
045    import org.kuali.rice.krad.service.KRADServiceLocator;
046    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
047    import org.kuali.rice.krad.service.KualiModuleService;
048    import org.kuali.rice.krad.service.ModuleService;
049    import org.kuali.rice.krad.util.GlobalVariables;
050    import org.kuali.rice.krad.util.KRADConstants;
051    import org.kuali.rice.krad.util.KRADUtils;
052    import org.kuali.rice.krad.util.UrlFactory;
053    
054    import javax.servlet.http.HttpServletRequest;
055    import javax.servlet.http.HttpServletResponse;
056    import java.util.Arrays;
057    import java.util.Enumeration;
058    import java.util.HashMap;
059    import java.util.HashSet;
060    import java.util.Map;
061    import java.util.Properties;
062    import java.util.Set;
063    
064    /**
065     * <p>The base {@link org.apache.struts.action.Action} class for all KNS-based Actions. Extends from the standard 
066     * {@link org.apache.struts.actions.DispatchAction} which allows for a <i>methodToCall</i> request parameter to
067     * be used to indicate which method to invoke.</p>
068     * 
069     * <p>This Action overrides #execute to set methodToCall for image submits.  Also performs other setup
070     * required for KNS framework calls.</p>
071     *
072     * @author Kuali Rice Team (rice.collab@kuali.org)
073     */
074    public abstract class KualiAction extends DispatchAction {
075        private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiAction.class);
076    
077        private static KualiModuleService kualiModuleService = null;
078        private static BusinessObjectAuthorizationService businessObjectAuthorizationService = null;
079        private static EncryptionService encryptionService = null;
080        private static Boolean OUTPUT_ENCRYPTION_WARNING = null;
081        private static String applicationBaseUrl = null;
082        
083        private Set<String> methodToCallsToNotCheckAuthorization = new HashSet<String>();
084        
085        {
086            methodToCallsToNotCheckAuthorization.add( "performLookup" );
087            methodToCallsToNotCheckAuthorization.add( "performQuestion" );
088            methodToCallsToNotCheckAuthorization.add( "performQuestionWithInput" );
089            methodToCallsToNotCheckAuthorization.add( "performQuestionWithInputAgainBecauseOfErrors" );
090            methodToCallsToNotCheckAuthorization.add( "performQuestionWithoutInput" );
091            methodToCallsToNotCheckAuthorization.add( "performWorkgroupLookup" );
092        }
093        
094        /**
095         * Entry point to all actions.
096         *
097         * NOTE: No need to hook into execute for handling framework setup anymore. Just implement the methodToCall for the framework
098         * setup, Constants.METHOD_REQUEST_PARAMETER will contain the full parameter, which can be sub stringed for getting framework
099         * parameters.
100         *
101         * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm,
102         *      javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
103         */
104        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
105            ActionForward returnForward = null;
106    
107            String methodToCall = findMethodToCall(form, request);
108            
109            if(isModuleLocked(form, methodToCall, request)) {
110                    return mapping.findForward(RiceConstants.MODULE_LOCKED_MAPPING);
111            }
112            
113            if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getMethodToCall())) {
114                if (StringUtils.isNotBlank(getImageContext(request, KRADConstants.ANCHOR))) {
115                    ((KualiForm) form).setAnchor(getImageContext(request, KRADConstants.ANCHOR));
116                }
117                else if (StringUtils.isNotBlank(request.getParameter(KRADConstants.ANCHOR))) {
118                    ((KualiForm) form).setAnchor(request.getParameter(KRADConstants.ANCHOR));
119                }
120                else {
121                    ((KualiForm) form).setAnchor(KRADConstants.ANCHOR_TOP_OF_FORM);
122                }
123            }
124            // if found methodToCall, pass control to that method, else return the basic forward
125            if (StringUtils.isNotBlank(methodToCall)) {
126                    if ( LOG.isDebugEnabled() ) {
127                            LOG.debug("methodToCall: '" + methodToCall+"'");
128                    }
129                returnForward = dispatchMethod(mapping, form, request, response, methodToCall);
130            }
131            else {
132                returnForward = defaultDispatch(mapping, form, request, response);
133            }
134    
135            // make sure the user can do what they're trying to according to the module that owns the functionality
136            if ( !methodToCallsToNotCheckAuthorization.contains(methodToCall) ) {
137                    if ( LOG.isDebugEnabled() ) {
138                            LOG.debug( "'" + methodToCall + "' not in set of excempt methods: " + methodToCallsToNotCheckAuthorization);
139                    }
140                    checkAuthorization(form, methodToCall);
141            } else {
142                    if ( LOG.isDebugEnabled() ) {
143                            LOG.debug("'" + methodToCall + "' is exempt from auth checks." );
144                    }
145            }
146    
147            // Add the ActionForm to GlobalVariables
148            // This will allow developers to retrieve both the Document and any request parameters that are not
149            // part of the Form and make them available in ValueFinder classes and other places where they are needed.
150            if(KNSGlobalVariables.getKualiForm() == null) {
151                    KNSGlobalVariables.setKualiForm((KualiForm)form);
152            }
153    
154            return returnForward;
155        }
156        
157        /**
158         * When no methodToCall is specified, the defaultDispatch method is invoked.  Default implementation
159         * returns the "basic" ActionForward.
160         */
161        protected ActionForward defaultDispatch(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
162            return mapping.findForward(RiceConstants.MAPPING_BASIC);
163        }
164    
165        @Override
166        protected ActionForward dispatchMethod(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String methodToCall) throws Exception {
167            GlobalVariables.getUserSession().addObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_OBJECT_KEY, methodToCall);
168            return super.dispatchMethod(mapping, form, request, response, methodToCall);
169        }
170        
171        protected String findMethodToCall(ActionForm form, HttpServletRequest request) throws Exception {
172            String methodToCall;
173            if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getMethodToCall())) {
174                methodToCall = ((KualiForm) form).getMethodToCall();
175            }
176            else {
177                // call utility method to parse the methodToCall from the request.
178                methodToCall = WebUtils.parseMethodToCall(form, request);
179            }
180            return methodToCall;
181        }
182    
183        /**
184         * Toggles the tab state in the ui
185         *
186         * @param mapping
187         * @param form
188         * @param request
189         * @param response
190         * @return
191         * @throws Exception
192         */
193        public ActionForward toggleTab(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
194            KualiForm kualiForm = (KualiForm) form;
195            String tabToToggle = getTabToToggle(request);
196            if (StringUtils.isNotBlank(tabToToggle)) {
197                if (kualiForm.getTabState(tabToToggle).equals("OPEN")) {
198                    kualiForm.getTabStates().remove(tabToToggle);
199                    kualiForm.getTabStates().put(tabToToggle, "CLOSE");
200                }
201                else {
202                    kualiForm.getTabStates().remove(tabToToggle);
203                    kualiForm.getTabStates().put(tabToToggle, "OPEN");
204                }
205            }
206    
207            doProcessingAfterPost( kualiForm, request );
208            return mapping.findForward(RiceConstants.MAPPING_BASIC);
209        }
210    
211        /**
212         * Toggles all tabs to open
213         *
214         * @param mapping
215         * @param form
216         * @param request
217         * @param response
218         * @return
219         * @throws Exception
220         */
221        public ActionForward showAllTabs(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
222            return this.doTabOpenOrClose(mapping, form, request, response, true);
223        }
224    
225        /**
226         * Toggles all tabs to closed
227         *
228         * @param mapping
229         * @param form
230         * @param request
231         * @param response
232         * @return
233         * @throws Exception
234         */
235        public ActionForward hideAllTabs(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
236            return this.doTabOpenOrClose(mapping, form, request, response, false);
237        }
238        
239        /**
240         * 
241         * Toggles all tabs to open of closed depending on the boolean flag.
242         * 
243         * @param mapping the mapping
244         * @param form the form
245         * @param request the request
246         * @param response the response
247         * @param open whether to open of close the tabs
248         * @return the action forward
249         */
250        private ActionForward doTabOpenOrClose(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, boolean open) {
251            KualiForm kualiForm = (KualiForm) form;
252    
253            Map<String, String> tabStates = kualiForm.getTabStates();
254            Map<String, String> newTabStates = new HashMap<String, String>();
255            for (String tabKey: tabStates.keySet()) {
256                    newTabStates.put(tabKey, open ? "OPEN" : "CLOSE");
257            }
258            kualiForm.setTabStates(newTabStates);
259            doProcessingAfterPost( kualiForm, request );
260            return mapping.findForward(RiceConstants.MAPPING_BASIC);
261        }
262    
263        /**
264         * Default refresh method. Called from returning frameworks.
265         *
266         * @param mapping
267         * @param form
268         * @param request
269         * @param response
270         * @return
271         * @throws Exception
272         */
273        public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
274            return mapping.findForward(RiceConstants.MAPPING_BASIC);
275        }
276    
277    
278        /**
279         * Parses the method to call attribute to pick off the line number which should be deleted.
280         *
281         * @param request
282         * @return
283         */
284        protected int getLineToDelete(HttpServletRequest request) {
285            return getSelectedLine(request);
286        }
287    
288        /**
289         * Parses the method to call attribute to pick off the line number which should have an action performed on it.
290         *
291         * @param request
292         * @return
293         */
294        protected int getSelectedLine(HttpServletRequest request) {
295            int selectedLine = -1;
296            String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
297            if (StringUtils.isNotBlank(parameterName)) {
298                String lineNumber = StringUtils.substringBetween(parameterName, ".line", ".");
299                if (StringUtils.isEmpty(lineNumber)) {
300                    return selectedLine;
301                }
302                selectedLine = Integer.parseInt(lineNumber);
303            }
304    
305            return selectedLine;
306        }
307    
308        /**
309         * Determines which tab was requested to be toggled
310         *
311         * @param request
312         * @return
313         */
314        protected String getTabToToggle(HttpServletRequest request) {
315            String tabToToggle = "";
316            String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
317            if (StringUtils.isNotBlank(parameterName)) {
318                tabToToggle = StringUtils.substringBetween(parameterName, ".tab", ".");
319            }
320    
321            return tabToToggle;
322        }
323    
324        /**
325         * Retrieves the header tab to navigate to.
326         *
327         * @param request
328         * @return
329         */
330        protected String getHeaderTabNavigateTo(HttpServletRequest request) {
331            String headerTabNavigateTo = RiceConstants.MAPPING_BASIC;
332            String imageContext = getImageContext(request, KRADConstants.NAVIGATE_TO);
333            if (StringUtils.isNotBlank(imageContext)) {
334                headerTabNavigateTo = imageContext;
335            }
336            return headerTabNavigateTo;
337        }
338    
339        /**
340         * Retrieves the header tab dispatch.
341         *
342         * @param request
343         * @return
344         */
345        protected String getHeaderTabDispatch(HttpServletRequest request) {
346            String headerTabDispatch = null;
347            String imageContext = getImageContext(request, KRADConstants.HEADER_DISPATCH);
348            if (StringUtils.isNotBlank(imageContext)) {
349                headerTabDispatch = imageContext;
350            }
351            else {
352                // In some cases it might be in request params instead
353                headerTabDispatch = request.getParameter(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
354            }
355            return headerTabDispatch;
356        }
357    
358        /**
359         * Retrieves the image context
360         *
361         * @param request
362         * @param contextKey
363         * @return
364         */
365        protected String getImageContext(HttpServletRequest request, String contextKey) {
366            String imageContext = "";
367            String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
368            if (StringUtils.isBlank(parameterName)) {
369                parameterName = request.getParameter("methodToCallPath");
370            }
371            if (StringUtils.isNotBlank(parameterName)) {
372                imageContext = StringUtils.substringBetween(parameterName, contextKey, ".");
373            }
374            return imageContext;
375        }
376    
377        protected String getReturnLocation(HttpServletRequest request, ActionMapping mapping) {
378            String mappingPath = mapping.getPath();
379            String basePath = getApplicationBaseUrl();
380            return basePath + ("/lookup".equals(mappingPath) || "/maintenance".equals(mappingPath) || "/multipleValueLookup".equals(mappingPath) ? "/kr" : "") + mappingPath + ".do";
381        }
382    
383        /**
384         * 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
385         * 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
386         * the property.
387         * 
388         * @param boClass a class implementing boClass, representing the BO that will be looked up
389         * @param parameterName the name of the parameter
390         * @param parameterValuePropertyName the property (relative to the form object) where the value to be passed into the lookup/inquiry may be found
391         * @param form
392         * @param request
393         * @return
394         */
395        protected String retrieveLookupParameterValue(Class<? extends BusinessObject> boClass, String parameterName, String parameterValuePropertyName, ActionForm form, HttpServletRequest request) throws Exception {
396            String value;
397            if (StringUtils.contains(parameterValuePropertyName, "'")) {
398                    value = StringUtils.replace(parameterValuePropertyName, "'", "");
399            } else if (request.getParameterMap().containsKey(parameterValuePropertyName)) {
400                    value = request.getParameter(parameterValuePropertyName);
401            } else if (request.getParameterMap().containsKey(KewApiConstants.DOCUMENT_ATTRIBUTE_FIELD_PREFIX + parameterValuePropertyName)) {
402                value = request.getParameter(KewApiConstants.DOCUMENT_ATTRIBUTE_FIELD_PREFIX + parameterValuePropertyName);
403            } else {
404                    if (form instanceof KualiForm) {
405                            value = ((KualiForm) form).retrieveFormValueForLookupInquiryParameters(parameterName, parameterValuePropertyName);
406                    } else {
407                            if (LOG.isDebugEnabled()) {
408                                    LOG.debug("Unable to retrieve lookup/inquiry parameter value for parameter name " + parameterName + " parameter value property " + parameterValuePropertyName);
409                            }
410                            value = null;
411                    }
412            }
413            
414            if (value != null && boClass != null && getBusinessObjectAuthorizationService().attributeValueNeedsToBeEncryptedOnFormsAndLinks(boClass, parameterName)) {
415                    value = getEncryptionService().encrypt(value) + EncryptionService.ENCRYPTION_POST_PREFIX;
416            }
417            return value;
418        }
419        
420        /**
421         * Takes care of storing the action form in the User session and forwarding to the lookup action.
422         *
423         * @param mapping
424         * @param form
425         * @param request
426         * @param response
427         * @return
428         * @throws Exception
429         */
430        @SuppressWarnings("unchecked")
431            public ActionForward performLookup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
432            // parse out the important strings from our methodToCall parameter
433            String fullParameter = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
434            validateLookupInquiryFullParameter(request, form, fullParameter);
435            
436            KualiForm kualiForm = (KualiForm) form;
437            
438            // when we return from the lookup, our next request's method to call is going to be refresh
439            kualiForm.registerEditableProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER);
440            
441            // parse out the baseLookupUrl if there is one
442            String baseLookupUrl = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM14_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM14_RIGHT_DEL);
443            
444            // parse out business object class name for lookup
445            String boClassName = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL, KRADConstants.METHOD_TO_CALL_BOPARM_RIGHT_DEL);
446            if (StringUtils.isBlank(boClassName)) {
447                throw new RuntimeException("Illegal call to perform lookup, no business object class name specified.");
448            }
449            Class boClass = null;
450                    try{
451                            boClass = Class.forName(boClassName);
452                    } catch(ClassNotFoundException cnfex){
453                throw new IllegalArgumentException("The classname (" + boClassName + ") does not represent a valid class which this application understands.");
454                    }
455                    
456            // build the parameters for the lookup url
457            Properties parameters = new Properties();
458            String conversionFields = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL);
459            if (StringUtils.isNotBlank(conversionFields)) {
460                parameters.put(KRADConstants.CONVERSION_FIELDS_PARAMETER, conversionFields);
461                
462                // register each of the destination parameters of the field conversion string as editable
463                String[] fieldConversions = conversionFields.split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
464                for (String fieldConversion : fieldConversions) {
465                    String destination = fieldConversion.split(KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR, 2)[1];
466                    kualiForm.registerEditableProperty(destination);
467                }
468            }
469    
470            // pass values from form that should be pre-populated on lookup search
471            String parameterFields = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM2_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM2_RIGHT_DEL);
472            if ( LOG.isDebugEnabled() ) {
473                LOG.debug( "fullParameter: " + fullParameter );
474                LOG.debug( "parameterFields: " + parameterFields );
475            }
476            if (StringUtils.isNotBlank(parameterFields)) {
477                String[] lookupParams = parameterFields.split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
478                if ( LOG.isDebugEnabled() ) {
479                     LOG.debug( "lookupParams: " + Arrays.toString(lookupParams) ); 
480                }
481                for (String lookupParam : lookupParams) {
482                    String[] keyValue = lookupParam.split(KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR, 2);
483                    if (keyValue.length != 2) {
484                                            throw new RuntimeException("malformed field conversion pair: " + Arrays.toString(keyValue));
485                                    } 
486    
487                    String lookupParameterValue = retrieveLookupParameterValue(boClass, keyValue[1], keyValue[0], form, request);
488                    if (StringUtils.isNotBlank(lookupParameterValue)) {
489                            parameters.put(keyValue[1], lookupParameterValue);
490                    }
491    
492                    if ( LOG.isDebugEnabled() ) {
493                        LOG.debug( "keyValue[0]: " + keyValue[0] );
494                        LOG.debug( "keyValue[1]: " + keyValue[1] );
495                    }
496                }
497            }
498    
499            // pass values from form that should be read-Only on lookup search
500            String readOnlyFields = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM8_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM8_RIGHT_DEL);
501            if (StringUtils.isNotBlank(readOnlyFields)) {
502                parameters.put(KRADConstants.LOOKUP_READ_ONLY_FIELDS, readOnlyFields);
503            }
504    
505            if ( LOG.isDebugEnabled() ) {
506                LOG.debug( "fullParameter: " + fullParameter );
507                LOG.debug( "readOnlyFields: " + readOnlyFields );
508            }
509    
510            // grab whether or not the "return value" link should be hidden or not
511            String hideReturnLink = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM3_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM3_RIGHT_DEL);
512            if (StringUtils.isNotBlank(hideReturnLink)) {
513                parameters.put(KRADConstants.HIDE_LOOKUP_RETURN_LINK, hideReturnLink);
514            }
515    
516            // add the optional extra button source and parameters string
517            String extraButtonSource = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM4_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM4_RIGHT_DEL);
518            if (StringUtils.isNotBlank(extraButtonSource)) {
519                parameters.put(KRADConstants.EXTRA_BUTTON_SOURCE, extraButtonSource);
520            }
521            String extraButtonParams = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM5_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM5_RIGHT_DEL);
522            if (StringUtils.isNotBlank(extraButtonParams)) {
523                parameters.put(KRADConstants.EXTRA_BUTTON_PARAMS, extraButtonParams);
524            }
525    
526            String lookupAction = KRADConstants.LOOKUP_ACTION;
527    
528            // is this a multi-value return?
529            boolean isMultipleValue = false;
530            String multipleValues = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM6_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM6_RIGHT_DEL);
531            if ((new Boolean(multipleValues).booleanValue())) {
532                parameters.put(KRADConstants.MULTIPLE_VALUE, multipleValues);
533                lookupAction = KRADConstants.MULTIPLE_VALUE_LOOKUP_ACTION;
534                isMultipleValue = true;
535            }
536    
537            // the name of the collection being looked up (primarily for multivalue lookups
538            String lookedUpCollectionName = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM11_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM11_RIGHT_DEL);
539            if (StringUtils.isNotBlank(lookedUpCollectionName)) {
540                parameters.put(KRADConstants.LOOKED_UP_COLLECTION_NAME, lookedUpCollectionName);
541            }
542    
543            // grab whether or not the "supress actions" column should be hidden or not
544            String supressActions = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM7_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM7_RIGHT_DEL);
545            if (StringUtils.isNotBlank(supressActions)) {
546                parameters.put(KRADConstants.SUPPRESS_ACTIONS, supressActions);
547            }
548    
549            // grab the references that should be refreshed upon returning from the lookup
550            String referencesToRefresh = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM10_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM10_RIGHT_DEL);
551            if (StringUtils.isNotBlank(referencesToRefresh)) {
552                parameters.put(KRADConstants.REFERENCES_TO_REFRESH, referencesToRefresh);
553            }
554    
555            // anchor, if it exists
556            if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
557                parameters.put(KRADConstants.LOOKUP_ANCHOR, ((KualiForm) form).getAnchor());
558            }
559    
560            // now add required parameters
561            parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, "start");
562    
563            // pass value from form that shows if autoSearch is desired for lookup search
564            String autoSearch = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM9_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM9_RIGHT_DEL);
565    
566            if (StringUtils.isNotBlank(autoSearch)) {
567                parameters.put(KRADConstants.LOOKUP_AUTO_SEARCH, autoSearch);
568                if ("YES".equalsIgnoreCase(autoSearch)){
569                    parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, "search");
570                }
571            }
572    
573            parameters.put(KRADConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObjectWithGeneratedKey(form));
574            parameters.put(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, boClassName);
575    
576            parameters.put(KRADConstants.RETURN_LOCATION_PARAMETER, getReturnLocation(request, mapping));
577            
578            if (form instanceof KualiDocumentFormBase) {
579                    String docNum = ((KualiDocumentFormBase) form).getDocument().getDocumentNumber();
580                            if(docNum != null){
581                                    parameters.put(KRADConstants.DOC_NUM, docNum);
582                            }
583                    }else if(form instanceof LookupForm){
584                            String docNum = ((LookupForm) form).getDocNum();
585                            if(docNum != null){
586                                    parameters.put(KRADConstants.DOC_NUM, ((LookupForm) form).getDocNum());
587                            }
588            }
589    
590            if (boClass != null) {
591                    ModuleService responsibleModuleService = getKualiModuleService().getResponsibleModuleService(boClass);
592                    if(responsibleModuleService!=null && responsibleModuleService.isExternalizable(boClass)){
593                            Map<String, String> parameterMap = new HashMap<String, String>();
594                            Enumeration<Object> e = parameters.keys();
595                            while (e.hasMoreElements()) {
596                                    String paramName = (String) e.nextElement();
597                                    parameterMap.put(paramName, parameters.getProperty(paramName));
598                            }
599                            return new ActionForward(responsibleModuleService.getExternalizableBusinessObjectLookupUrl(boClass, parameterMap), true);
600                    }
601            }
602                    
603            if (StringUtils.isBlank(baseLookupUrl)) {
604                    baseLookupUrl = getApplicationBaseUrl() + "/kr/" + lookupAction;
605            } else {
606                    if (isMultipleValue) {
607                            LookupUtils.transformLookupUrlToMultiple(baseLookupUrl);
608                    }
609            }
610            String lookupUrl = UrlFactory.parameterizeUrl(baseLookupUrl, parameters);
611            return new ActionForward(lookupUrl, true);
612        }
613    
614        protected void validateLookupInquiryFullParameter(HttpServletRequest request, ActionForm form, String fullParameter){
615            PojoFormBase pojoFormBase = (PojoFormBase) form;
616            if(WebUtils.isFormSessionDocument((PojoFormBase) form)){
617                    if(!pojoFormBase.isPropertyEditable(fullParameter)) {
618                            throw new RuntimeException("The methodToCallAttribute is not registered as an editable property.");
619                    }
620            }
621        }
622        
623        @SuppressWarnings("unchecked")
624            public ActionForward performInquiry(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
625            // parse out the important strings from our methodToCall parameter
626            String fullParameter = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
627            validateLookupInquiryFullParameter(request, form, fullParameter);
628            
629            // when javascript is disabled, the inquiry will appear in the same window as the document.  when we close the inquiry, 
630            // our next request's method to call is going to be refresh
631            KualiForm kualiForm = (KualiForm) form;
632            kualiForm.registerEditableProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER);
633            
634            // parse out business object class name for lookup
635            String boClassName = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL, KRADConstants.METHOD_TO_CALL_BOPARM_RIGHT_DEL);
636            if (StringUtils.isBlank(boClassName)) {
637                throw new RuntimeException("Illegal call to perform inquiry, no business object class name specified.");
638            }
639    
640            // build the parameters for the inquiry url
641            Properties parameters = new Properties();
642            parameters.put(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, boClassName);
643    
644            parameters.put(KRADConstants.RETURN_LOCATION_PARAMETER, getReturnLocation(request, mapping));
645    
646            // pass values from form that should be pre-populated on inquiry
647            String parameterFields = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM2_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM2_RIGHT_DEL);
648            if ( LOG.isDebugEnabled() ) {
649                LOG.debug( "fullParameter: " + fullParameter );
650                LOG.debug( "parameterFields: " + parameterFields );
651            }
652            if (StringUtils.isNotBlank(parameterFields)) {
653                // TODO : create a method for this to be used by both lookup & inquiry ?
654                String[] inquiryParams = parameterFields.split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
655                if ( LOG.isDebugEnabled() ) {
656                    LOG.debug( "inquiryParams: " + inquiryParams );
657                }
658                Class<? extends BusinessObject> boClass = (Class<? extends BusinessObject>) Class.forName(boClassName);
659                for (String inquiryParam : inquiryParams) {
660                    String[] keyValue = inquiryParam.split(KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR, 2);
661    
662                    String inquiryParameterValue = retrieveLookupParameterValue(boClass, keyValue[1], keyValue[0], form, request);
663                    if (inquiryParameterValue == null) {
664                            parameters.put(keyValue[1], "directInquiryKeyNotSpecified");
665                    }
666                    else {
667                            parameters.put(keyValue[1], inquiryParameterValue);
668                    }
669    
670                    if ( LOG.isDebugEnabled() ) {
671                        LOG.debug( "keyValue[0]: " + keyValue[0] );
672                        LOG.debug( "keyValue[1]: " + keyValue[1] );
673                    }
674                }
675            }
676            parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, "start");
677            parameters.put(KRADConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObjectWithGeneratedKey(form));
678            String inquiryUrl = null;
679            try {
680                    Class.forName(boClassName);
681                    inquiryUrl = getApplicationBaseUrl() + "/kr/" + KRADConstants.DIRECT_INQUIRY_ACTION;
682            } catch ( ClassNotFoundException ex ) {
683                // allow inquiry url to be null (and therefore no inquiry link will be displayed) but at least log a warning
684                    LOG.warn("Class name does not represent a valid class which this application understands: " + boClassName);
685            }
686            inquiryUrl = UrlFactory.parameterizeUrl(inquiryUrl, parameters);
687            return new ActionForward(inquiryUrl, true);
688    
689        }
690    
691        /**
692         * This method handles rendering the question component, but without any of the extra error fields
693         *
694         * @param mapping
695         * @param form
696         * @param request
697         * @param response
698         * @param questionId
699         * @param questionText
700         * @param questionType
701         * @param caller
702         * @param context
703         * @return ActionForward
704         * @throws Exception
705         */
706        protected ActionForward performQuestionWithoutInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionId, String questionText, String questionType, String caller, String context) throws Exception {
707            return performQuestion(mapping, form, request, response, questionId, questionText, questionType, caller, context, false, "", "", "", "");
708        }
709    
710        /**
711         * Handles rendering a question prompt - without a specified context.
712         *
713         * @param mapping
714         * @param form
715         * @param request
716         * @param response
717         * @param questionId
718         * @param questionText
719         * @param questionType
720         * @param caller
721         * @param context
722         * @return ActionForward
723         * @throws Exception
724         */
725        protected ActionForward performQuestionWithInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionId, String questionText, String questionType, String caller, String context) throws Exception {
726            return performQuestion(mapping, form, request, response, questionId, questionText, questionType, caller, context, true, "", "", "", "");
727        }
728    
729        /**
730         * Handles re-rendering a question prompt because of an error on what was submitted.
731         *
732         * @param mapping
733         * @param form
734         * @param request
735         * @param response
736         * @param questionId
737         * @param questionText
738         * @param questionType
739         * @param caller
740         * @param context
741         * @param reason
742         * @param errorKey
743         * @param errorPropertyName
744         * @param errorParameter
745         * @return ActionForward
746         * @throws Exception
747         */
748        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 {
749            return performQuestion(mapping, form, request, response, questionId, questionText, questionType, caller, context, true, reason, errorKey, errorPropertyName, errorParameter);
750        }
751    
752        /**
753         * Handles rendering a question prompt - with a specified context.
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 showReasonField
765         * @param reason
766         * @param errorKey
767         * @param errorPropertyName
768         * @param errorParameter
769         * @return ActionForward
770         * @throws Exception
771         */
772        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 {
773            Properties parameters = new Properties();
774    
775            parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, "start");
776            parameters.put(KRADConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObjectWithGeneratedKey(form));
777            parameters.put(KRADConstants.CALLING_METHOD, caller);
778            parameters.put(KRADConstants.QUESTION_INST_ATTRIBUTE_NAME, questionId);
779            parameters.put(KRADConstants.QUESTION_IMPL_ATTRIBUTE_NAME, questionType);
780            parameters.put(KRADConstants.QUESTION_TEXT_ATTRIBUTE_NAME, questionText);
781            parameters.put(KRADConstants.RETURN_LOCATION_PARAMETER, getReturnLocation(request, mapping));
782            parameters.put(KRADConstants.QUESTION_CONTEXT, context);
783            parameters.put(KRADConstants.QUESTION_SHOW_REASON_FIELD, Boolean.toString(showReasonField));
784            parameters.put(KRADConstants.QUESTION_REASON_ATTRIBUTE_NAME, reason);
785            parameters.put(KRADConstants.QUESTION_ERROR_KEY, errorKey);
786            parameters.put(KRADConstants.QUESTION_ERROR_PROPERTY_NAME, errorPropertyName);
787            parameters.put(KRADConstants.QUESTION_ERROR_PARAMETER, errorParameter);
788            parameters.put(KRADConstants.QUESTION_ANCHOR, form instanceof KualiForm ? ObjectUtils.toString(((KualiForm) form).getAnchor()) : "");
789            Object methodToCallAttribute = request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
790            if (methodToCallAttribute != null) {
791                parameters.put(KRADConstants.METHOD_TO_CALL_PATH, methodToCallAttribute);
792                ((PojoForm) form).registerEditableProperty(String.valueOf(methodToCallAttribute));
793            }
794            
795            if (form instanceof KualiDocumentFormBase) {
796                    String docNum = ((KualiDocumentFormBase) form).getDocument().getDocumentNumber();
797                    if(docNum != null){
798                            parameters.put(KRADConstants.DOC_NUM, ((KualiDocumentFormBase) form)
799                                            .getDocument().getDocumentNumber());
800                    }
801                    }
802    
803            String questionUrl = UrlFactory.parameterizeUrl(getApplicationBaseUrl() + "/kr/" + KRADConstants.QUESTION_ACTION, parameters);
804            return new ActionForward(questionUrl, true);
805        }
806    
807    
808        /**
809         * Takes care of storing the action form in the User session and forwarding to the workflow workgroup lookup action.
810         *
811         * @param mapping
812         * @param form
813         * @param request
814         * @param response
815         * @return
816         * @throws Exception
817         */
818        public ActionForward performWorkgroupLookup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
819            String returnUrl = null;
820            if ("/kr".equals(mapping.getModuleConfig().getPrefix())) {
821                    returnUrl = getApplicationBaseUrl() + mapping.getModuleConfig().getPrefix() + mapping.getPath() + ".do";
822            } else {
823                    returnUrl = getApplicationBaseUrl() + mapping.getPath() + ".do";
824            }
825    
826    
827            String fullParameter = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
828            String conversionFields = StringUtils.substringBetween(fullParameter, KRADConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL);
829    
830            String deploymentBaseUrl = KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
831                    KRADConstants.WORKFLOW_URL_KEY);
832            String workgroupLookupUrl = deploymentBaseUrl + "/Lookup.do?lookupableImplServiceName=WorkGroupLookupableImplService&methodToCall=start&docFormKey=" + GlobalVariables.getUserSession().addObjectWithGeneratedKey(form);
833    
834            if (conversionFields != null) {
835                workgroupLookupUrl += "&conversionFields=" + conversionFields;
836            }
837            if (form instanceof KualiDocumentFormBase) {
838                            workgroupLookupUrl +="&docNum="+ ((KualiDocumentFormBase) form).getDocument().getDocumentNumber();
839                    }
840            
841            workgroupLookupUrl += "&returnLocation=" + returnUrl;
842    
843            return new ActionForward(workgroupLookupUrl, true);
844        }
845    
846        /**
847         * Handles requests that originate via Header Tabs.
848         *
849         * @param mapping
850         * @param form
851         * @param request
852         * @param response
853         * @return
854         * @throws Exception
855         */
856        public ActionForward headerTab(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
857            // header tab actions can do two things - 1, call into an action and perform what needs to happen in there and 2, forward to
858            // a new location.
859            String headerTabDispatch = getHeaderTabDispatch(request);
860            if (StringUtils.isNotEmpty(headerTabDispatch)) {
861                ActionForward forward = dispatchMethod(mapping, form, request, response, headerTabDispatch);
862                if (GlobalVariables.getMessageMap().getNumberOfPropertiesWithErrors() > 0) {
863                    return mapping.findForward(RiceConstants.MAPPING_BASIC);
864                }
865                this.doTabOpenOrClose(mapping, form, request, response, false);
866                if (forward.getRedirect()) {
867                    return forward;
868                }
869            }
870            return dispatchMethod(mapping, form, request, response, getHeaderTabNavigateTo(request));
871        }
872    
873        /**
874         * Override this method to provide action-level access controls to the application.
875         *
876         * @param form
877         * @throws AuthorizationException
878         */
879        protected void checkAuthorization( ActionForm form, String methodToCall) throws AuthorizationException 
880        {
881            String principalId = GlobalVariables.getUserSession().getPrincipalId();
882            Map<String, String> roleQualifier = new HashMap<String, String>(getRoleQualification(form, methodToCall));
883            Map<String, String> permissionDetails = KRADUtils.getNamespaceAndActionClass(this.getClass());
884            
885            if (!KimApiServiceLocator.getPermissionService().isAuthorizedByTemplate(principalId,
886                    KRADConstants.KNS_NAMESPACE, KimConstants.PermissionTemplateNames.USE_SCREEN, permissionDetails,
887                    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, 2);
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            
1140            protected boolean isModuleLocked(ActionForm form, String methodToCall, HttpServletRequest request) {
1141                    String boClass = request.getParameter(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE);
1142                    ModuleService moduleService = null;
1143                    if(StringUtils.isNotBlank(boClass)) {
1144                            try {
1145                                    moduleService = getKualiModuleService().getResponsibleModuleService(Class.forName(boClass));
1146                            } catch (ClassNotFoundException classNotFoundException) {
1147                                    LOG.warn("BO class not found: " + boClass, classNotFoundException);
1148                            }
1149                    } else {
1150                            moduleService = getKualiModuleService().getResponsibleModuleService(this.getClass());
1151                    }
1152                    if(moduleService != null && moduleService.isLocked()) {
1153                            String principalId = GlobalVariables.getUserSession().getPrincipalId();
1154                            String namespaceCode = KRADConstants.KUALI_RICE_SYSTEM_NAMESPACE;
1155                            String permissionName = KimConstants.PermissionNames.ACCESS_LOCKED_MODULE;
1156                            Map<String, String> qualification = getRoleQualification(form, methodToCall);
1157                            if(!KimApiServiceLocator.getPermissionService().isAuthorized(principalId, namespaceCode, permissionName, qualification)) {
1158                                    ParameterService parameterSerivce = CoreFrameworkServiceLocator.getParameterService();
1159                                    String messageParamNamespaceCode = moduleService.getModuleConfiguration().getNamespaceCode();
1160                                    String messageParamComponentCode = KRADConstants.DetailTypes.ALL_DETAIL_TYPE;
1161                                    String messageParamName = KRADConstants.SystemGroupParameterNames.OLTP_LOCKOUT_MESSAGE_PARM;
1162                                    String lockoutMessage = parameterSerivce.getParameterValueAsString(messageParamNamespaceCode, messageParamComponentCode, messageParamName);
1163                                    
1164                                    if(StringUtils.isBlank(lockoutMessage)) {
1165                                            String defaultMessageParamName = KRADConstants.SystemGroupParameterNames.OLTP_LOCKOUT_DEFAULT_MESSAGE;
1166                                            lockoutMessage = parameterSerivce.getParameterValueAsString(KRADConstants.KNS_NAMESPACE, messageParamComponentCode, defaultMessageParamName);
1167                                    }
1168                                    request.setAttribute(KRADConstants.MODULE_LOCKED_MESSAGE_REQUEST_PARAMETER, lockoutMessage);
1169                                    return true;
1170                            }
1171                    }
1172                    return false;
1173            }
1174    }