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.form;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.struts.action.ActionMapping;
020import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
021import org.kuali.rice.core.web.format.Formatter;
022import org.kuali.rice.kns.datadictionary.HeaderNavigation;
023import org.kuali.rice.kns.util.ActionFormUtilMap;
024import org.kuali.rice.kns.util.WebUtils;
025import org.kuali.rice.kns.web.struts.form.pojo.PojoFormBase;
026import org.kuali.rice.kns.web.ui.ExtraButton;
027import org.kuali.rice.kns.web.ui.HeaderField;
028import org.kuali.rice.krad.util.KRADConstants;
029import org.kuali.rice.krad.util.ObjectUtils;
030import org.springframework.util.AutoPopulatingList;
031
032import javax.servlet.ServletRequest;
033import javax.servlet.http.HttpServletRequest;
034import java.util.ArrayList;
035import java.util.HashMap;
036import java.util.List;
037import java.util.Map;
038
039/**
040 * This class common properites for all action forms.
041 */
042public class KualiForm extends PojoFormBase {
043    /**
044     * Tab state UI literals
045     */
046    public static enum TabState {
047        OPEN,
048        CLOSE
049    }
050
051    private static final long serialVersionUID = 1L;
052
053    private static final String literalPrefixAndDelimiter =
054        KRADConstants.LOOKUP_PARAMETER_LITERAL_PREFIX+ KRADConstants.LOOKUP_PARAMETER_LITERAL_DELIMITER;
055
056    private String backLocation;
057    private String methodToCall;
058    private String refreshCaller;
059    private String anchor;
060    private Map<String, String> tabStates;
061    private Map actionFormUtilMap;
062    private Map displayedErrors = new HashMap();
063    private Map<String, Object> displayedWarnings = new HashMap<String, Object>();
064    private Map<String, Object> displayedInfo = new HashMap<String, Object>();
065    private int currentTabIndex = 0;
066    private int arbitrarilyHighIndex = 1000000;
067
068    private String navigationCss;
069    private HeaderNavigation[] headerNavigationTabs;
070    protected List<ExtraButton> extraButtons = new AutoPopulatingList( ExtraButton.class ) ;
071
072    private boolean fieldLevelHelpEnabled;
073    
074    private List<HeaderField> docInfo;
075    private int numColumns = 2;
076    
077    private String fieldNameToFocusOnAfterSubmit;
078    
079    /**
080     * @see org.kuali.rice.krad.web.struts.pojo.PojoFormBase#addRequiredNonEditableProperties()
081     */
082    @Override
083    public void addRequiredNonEditableProperties(){
084        super.addRequiredNonEditableProperties();
085        registerRequiredNonEditableProperty(KRADConstants.REFRESH_CALLER);
086    }
087    
088    public int getNumColumns() {
089        return this.numColumns;
090    }
091
092    public void setNumColumns(int numColumns) {
093        this.numColumns = numColumns;
094    }
095
096    public List<HeaderField> getDocInfo() {
097        return this.docInfo;
098    }
099
100    public void setDocInfo(List<HeaderField> docInfo) {
101        this.docInfo = docInfo;
102    }
103
104    /**
105     * no args constructor which must init our tab states list
106     */
107    public KualiForm() {
108        this.tabStates = new HashMap<String, String>();
109        this.actionFormUtilMap = new ActionFormUtilMap();
110        this.docInfo = new ArrayList<HeaderField>();
111    }
112
113    /**
114     * Checks for methodToCall parameter, and if not populated in form calls utility method to parse the string from the request.
115     */
116    public void populate(HttpServletRequest request) {
117        setMethodToCall(WebUtils.parseMethodToCall(this, request));
118        
119        super.populate(request);
120
121        populateBackLocation(request);
122        populateFieldLevelHelpEnabled(request);
123        
124        if (actionFormUtilMap instanceof ActionFormUtilMap) {
125            ((ActionFormUtilMap) actionFormUtilMap).setCacheValueFinderResults(true);
126        }
127    }
128        
129    private static Boolean ENABLE_FIELD_LEVEL_HELP_IND;
130
131    protected void populateBackLocation(HttpServletRequest request){
132        if (getParameter(request, "returnLocation") != null) {
133            setBackLocation(getParameter(request, "returnLocation"));
134        }
135    }
136    
137    /**
138     * Populates whether the each field will have field-level help associated with it.  Depending on how the jsp/tags are implemented, the value
139     * populated by this method may be overruled by other settings
140     * 
141     * @param request
142     */
143    protected void populateFieldLevelHelpEnabled(HttpServletRequest request) {
144        boolean fieldLevelHelpEnabled = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean(
145                KRADConstants.KNS_NAMESPACE, KRADConstants.DetailTypes.ALL_DETAIL_TYPE,
146                KRADConstants.SystemGroupParameterNames.ENABLE_FIELD_LEVEL_HELP_IND, Boolean.FALSE);
147        setFieldLevelHelpEnabled(fieldLevelHelpEnabled);
148    }
149
150    public Map getDisplayedErrors() {
151        return displayedErrors;
152    }
153
154    /**
155         * @return the displayedWarnings
156         */
157        public Map<String, Object> getDisplayedWarnings() {
158                return this.displayedWarnings;
159        }
160
161        /**
162         * @return the displayedInfo
163         */
164        public Map<String, Object> getDisplayedInfo() {
165                return this.displayedInfo;
166        }
167
168        /**
169     * Used by the dispatch action to determine which action method to call into.
170     *
171     * @return Returns the methodToCall.
172     */
173    public String getMethodToCall() {
174        return methodToCall;
175    }
176
177
178    /**
179     * @param methodToCall The methodToCall to set.
180     */
181    public void setMethodToCall(String methodToCall) {
182        this.methodToCall = methodToCall;
183    }
184
185
186    /**
187     * Can be used by actions refresh method to determine what called the the refresh method.
188     *
189     * @return Returns the refreshCaller.
190     */
191    public String getRefreshCaller() {
192        return refreshCaller;
193    }
194
195
196    /**
197     * @param refreshCaller The refreshCaller to set.
198     */
199    public void setRefreshCaller(String refreshCaller) {
200        this.refreshCaller = refreshCaller;
201    }
202
203    /**
204     * @return the tab state list
205     */
206    public Map<String, String> getTabStates() {
207        return tabStates;
208    }
209
210    /**
211     * simple setter for the tab state Map
212     *
213     * @param tabStates
214     */
215    public void setTabStates(Map<String, String> tabStates) {
216        this.tabStates = tabStates;
217    }
218
219    /**
220     * Special getter based on key to work with multi rows for tab state objects
221     */
222    public String getTabState(String key) {
223        String state = KRADConstants.EMPTY_STRING;
224        if (tabStates.containsKey(key)) {
225            if (tabStates.get(key) instanceof String) {
226                state = tabStates.get(key);
227            }
228            else {
229                //This is the case where the value is an Array of String,
230                //so we'll have to get the first element
231                Object result = tabStates.get(key);
232                result.getClass();
233                state = ((String[])result)[0];
234            }
235        }
236
237        return state;
238    }
239
240    public int getCurrentTabIndex() {
241        return currentTabIndex;
242    }
243
244    public void setCurrentTabIndex(int currentTabIndex) {
245        this.currentTabIndex = currentTabIndex;
246    }
247
248    public void incrementTabIndex() {
249        this.currentTabIndex++;
250    }
251
252    public int getNextArbitrarilyHighIndex() {
253        return this.arbitrarilyHighIndex++;
254    }
255    
256        public String getFieldNameToFocusOnAfterSubmit() {
257                return this.fieldNameToFocusOnAfterSubmit;
258        }
259
260        public void setFieldNameToFocusOnAfterSubmit(String fieldNameToFocusOnAfterSubmit) {
261                this.fieldNameToFocusOnAfterSubmit = fieldNameToFocusOnAfterSubmit;
262        }
263
264        /**
265     * @return Returns the validOptionsMap.
266     */
267    public Map getActionFormUtilMap() {
268        return actionFormUtilMap;
269    }
270
271    /**
272     * @param validOptionsMap The validOptionsMap to set.
273     */
274    public void setActionFormUtilMap(Map validOptionsMap) {
275        this.actionFormUtilMap = validOptionsMap;
276    }
277
278    /**
279     * Gets the headerNavigationTabs attribute.
280     *
281     * @return Returns the headerNavigationTabs.
282     */
283    public HeaderNavigation[] getHeaderNavigationTabs() {
284        return headerNavigationTabs;
285    }
286
287    /**
288     * Sets the headerNavigationTabs attribute value.
289     *
290     * @param headerNavigationTabs The headerNavigationTabs to set.
291     */
292    public void setHeaderNavigationTabs(HeaderNavigation[] headerNavigationTabs) {
293        this.headerNavigationTabs = headerNavigationTabs;
294    }
295
296    /**
297     * Gets the navigationCss attribute.
298     *
299     * @return Returns the navigationCss.
300     */
301    public String getNavigationCss() {
302        return navigationCss;
303    }
304
305    /**
306     * Sets the navigationCss attribute value.
307     *
308     * @param navigationCss The navigationCss to set.
309     */
310    public void setNavigationCss(String navigationCss) {
311        this.navigationCss = navigationCss;
312    }
313
314    public String getAnchor() {
315        return anchor;
316    }
317
318    public void setAnchor(String anchor) {
319        this.anchor = anchor;
320    }
321
322    public List<ExtraButton> getExtraButtons() {
323        return extraButtons;
324    }
325
326    public void setExtraButtons(List<ExtraButton> extraButtons) {
327        if ( extraButtons instanceof AutoPopulatingList ) {
328            this.extraButtons = extraButtons;
329        } else {
330            this.extraButtons.clear();
331            this.extraButtons.addAll( extraButtons );
332        }
333    }
334
335    public ExtraButton getExtraButton( int index ) {
336        return extraButtons.get( index );
337    }
338
339    public void setExtraButton( int index, ExtraButton extraButton ) {
340        extraButtons.set( index, extraButton );
341    }
342
343    /**
344     * Returns whether field level help is enabled for this form.
345     * 
346     * @return
347     */
348    public boolean isFieldLevelHelpEnabled() {
349        return this.fieldLevelHelpEnabled;
350    }
351
352    public void setFieldLevelHelpEnabled(boolean fieldLevelHelpEnabled) {
353        this.fieldLevelHelpEnabled = fieldLevelHelpEnabled;
354    }
355    
356    
357    /**
358     * Retrieves a value from the form for the purposes of passing it as a parameter into the lookup or inquiry frameworks 
359     * 
360     * @param parameterName the name of the parameter, as expected by the lookup or inquiry frameworks
361     * @param parameterValueLocation the name of the property containing the value of the parameter
362     * @return the value of the parameter
363     */
364    public String retrieveFormValueForLookupInquiryParameters(String parameterName, String parameterValueLocation) {
365        // dereference literal values by simply trimming of the prefix
366        if (parameterValueLocation.startsWith(literalPrefixAndDelimiter)) {
367                return parameterValueLocation.substring(literalPrefixAndDelimiter.length());
368        }
369
370        Object value = ObjectUtils.getPropertyValue(this, parameterValueLocation);
371                if (value == null) {
372                        return null;
373                }
374                if (value instanceof String) {
375                        return (String) value;
376                }
377                Formatter formatter = Formatter.getFormatter(value.getClass());
378                return (String) formatter.format(value);        
379    }
380
381        /**
382         * This overridden method ...
383         * 
384         * @see org.kuali.rice.krad.web.struts.pojo.PojoFormBase#shouldPropertyBePopulatedInForm(java.lang.String, javax.servlet.http.HttpServletRequest)
385         */
386        @Override
387        public boolean shouldPropertyBePopulatedInForm(
388                        String requestParameterName, HttpServletRequest request) {
389                if (requestParameterName.startsWith(KRADConstants.TAB_STATES)) {
390                        return true;
391                }
392                
393                if (requestParameterName.equals(KRADConstants.DISPATCH_REQUEST_PARAMETER)) {
394                        String methodToCallParameterName = request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER);
395                        if(StringUtils.equals(methodToCallParameterName, KRADConstants.RETURN_METHOD_TO_CALL)){
396                                return true;
397                        }
398                }
399                
400                return super.shouldPropertyBePopulatedInForm(requestParameterName, request);
401        }
402    
403    public boolean shouldMethodToCallParameterBeUsed(String methodToCallParameterName, String methodToCallParameterValue, HttpServletRequest request) {
404        if ("GET".equalsIgnoreCase(request.getMethod())) {
405                return true;
406        }
407        if (shouldPropertyBePopulatedInForm(methodToCallParameterName, request)) {
408                return true;
409        }
410        if (methodToCallParameterName != null && WebUtils.endsWithCoordinates(methodToCallParameterName)) {
411                methodToCallParameterName = methodToCallParameterName.substring(0, WebUtils.getIndexOfCoordinateExtension(methodToCallParameterName));
412                if (shouldPropertyBePopulatedInForm(methodToCallParameterName, request)) {
413                        return true;
414                }
415        }
416        if (KRADConstants.METHOD_TO_CALL_PATH.equals(methodToCallParameterName)) {
417                if (shouldPropertyBePopulatedInForm(methodToCallParameterValue, request)) {
418                        return true;
419                }
420                if (methodToCallParameterValue != null && WebUtils.endsWithCoordinates(methodToCallParameterName)) {
421                        methodToCallParameterValue = methodToCallParameterValue.substring(0, WebUtils
422                        .getIndexOfCoordinateExtension(methodToCallParameterName));
423                        if (shouldPropertyBePopulatedInForm(methodToCallParameterValue, request)) {
424                                return true;
425                        }
426                }
427        }
428        return false;
429    }
430
431        /**
432         * @see org.kuali.rice.krad.web.struts.pojo.PojoFormBase#clearEditablePropertyInformation()
433         */
434        @Override
435        public void clearEditablePropertyInformation() {
436                super.clearEditablePropertyInformation();
437        }
438        
439        public void setDerivedValuesOnForm(HttpServletRequest request) {
440        }
441
442        /**
443         * @see org.apache.struts.action.ActionForm#reset(org.apache.struts.action.ActionMapping, javax.servlet.http.HttpServletRequest)
444         */
445        @Override
446        public void reset(ActionMapping mapping, HttpServletRequest request) {
447                super.reset(mapping, request);
448                if (extraButtons != null) {
449                        extraButtons.clear();
450                }
451                //fieldNameToFocusOnAfterSubmit = "";
452                clearDisplayedMessages();
453        }
454
455        /**
456         * @see org.apache.struts.action.ActionForm#reset(org.apache.struts.action.ActionMapping, javax.servlet.ServletRequest)
457         */
458        @Override
459        public void reset(ActionMapping mapping, ServletRequest request) {
460                super.reset(mapping, request);
461                if (extraButtons != null) {
462                        extraButtons.clear();
463                }
464                //fieldNameToFocusOnAfterSubmit = "";
465                clearDisplayedMessages();
466        }
467        
468        private void clearDisplayedMessages() {
469                if (displayedErrors != null) {
470                        displayedErrors.clear();
471                }
472                if (displayedWarnings != null) {
473                        displayedWarnings.clear();
474                }
475                if (displayedInfo != null) {
476                        displayedInfo.clear();
477                }
478        }
479        
480    /**
481         * @return the backLocation
482         */
483        public String getBackLocation() {
484                return WebUtils.sanitizeBackLocation(this.backLocation);
485        }
486
487        /**
488         * @param backLocation the backLocation to set
489         */
490        public void setBackLocation(String backLocation) {
491                this.backLocation = backLocation;
492        }
493
494}