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