001    /**
002     * Copyright 2005-2011 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.krad.web.form;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.codehaus.jackson.map.ObjectMapper;
020    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
021    import org.kuali.rice.krad.uif.UifConstants;
022    import org.kuali.rice.krad.uif.UifParameters;
023    import org.kuali.rice.krad.uif.view.History;
024    import org.kuali.rice.krad.uif.view.View;
025    import org.kuali.rice.krad.uif.service.ViewService;
026    import org.kuali.rice.krad.uif.view.ViewModel;
027    import org.kuali.rice.krad.util.KRADUtils;
028    import org.springframework.web.multipart.MultipartFile;
029    import org.kuali.rice.krad.uif.UifConstants.ViewType;
030    
031    import javax.servlet.http.HttpServletRequest;
032    import java.io.IOException;
033    import java.util.ArrayList;
034    import java.util.HashMap;
035    import java.util.List;
036    import java.util.Map;
037    import java.util.Properties;
038    import java.util.Set;
039    import java.util.UUID;
040    
041    /**
042     * Base form class for views within the KRAD User Interface Framework
043     *
044     * <p>
045     * Holds properties necessary to determine the <code>View</code> instance that
046     * will be used to render the UI
047     * </p>
048     *
049     * @author Kuali Rice Team (rice.collab@kuali.org)
050     */
051    public class UifFormBase implements ViewModel {
052        private static final long serialVersionUID = 8432543267099454434L;
053    
054        // current view
055        protected String viewId;
056        protected String viewName;
057        protected ViewType viewTypeName;
058        protected String pageId;
059        protected String methodToCall;
060        protected String formKey;
061        protected String jumpToId;
062        protected String jumpToName;
063        protected String focusId;
064        protected String formPostUrl;
065    
066        protected boolean defaultsApplied;
067    
068        protected View view;
069        protected View previousView;
070    
071        protected Map<String, String> viewRequestParameters;
072        protected List<String> readOnlyFieldsList;
073    
074        protected Map<String, Object> newCollectionLines;
075        protected Map<String, String> actionParameters;
076        protected Map<String, Object> clientStateForSyncing;
077        protected Map<String, Set<String>> selectedCollectionLines;
078    
079        protected MultipartFile attachmentFile;
080    
081        // navigation
082        protected String returnLocation;
083        protected String returnFormKey;
084    
085        protected History formHistory;
086    
087        protected boolean renderFullView;
088        protected boolean validateDirty;
089    
090        public UifFormBase() {
091            formKey = generateFormKey();
092            renderFullView = true;
093            defaultsApplied = false;
094    
095            readOnlyFieldsList = new ArrayList<String>();
096            viewRequestParameters = new HashMap<String, String>();
097            newCollectionLines = new HashMap<String, Object>();
098            actionParameters = new HashMap<String, String>();
099            clientStateForSyncing = new HashMap<String, Object>();
100            selectedCollectionLines = new HashMap<String, Set<String>>();
101        }
102    
103        /**
104         * Creates the unique id used to store this "conversation" in the session.
105         * The default method generates a java UUID.
106         *
107         * @return
108         */
109        protected String generateFormKey() {
110            return UUID.randomUUID().toString();
111        }
112    
113        /**
114         * Called after Spring binds the request to the form and before the
115         * controller method is invoked.
116         *
117         * @param request - request object containing the query parameters
118         */
119        public void postBind(HttpServletRequest request) {
120            // default form post URL to request URL
121            formPostUrl = request.getRequestURL().toString();
122    
123            // get any sent client view state and parse into map
124            if (request.getParameterMap().containsKey(UifParameters.CLIENT_VIEW_STATE)) {
125                String clientStateJSON = request.getParameter(UifParameters.CLIENT_VIEW_STATE);
126                if (StringUtils.isNotBlank(clientStateJSON)) {
127                    // change single quotes to double quotes (necessary because the reverse was done for sending)
128                    clientStateJSON = StringUtils.replace(clientStateJSON, "'", "\"");
129    
130                    ObjectMapper mapper = new ObjectMapper();
131                    try {
132                        clientStateForSyncing = mapper.readValue(clientStateJSON, Map.class);
133                    } catch (IOException e) {
134                        throw new RuntimeException("Unable to decode client side state JSON", e);
135                    }
136                }
137            }
138    
139            // populate read only fields list
140            if (request.getParameter(UifParameters.READ_ONLY_FIELDS) != null) {
141                String readOnlyFields = request.getParameter(UifParameters.READ_ONLY_FIELDS);
142                setReadOnlyFieldsList(KRADUtils.convertStringParameterToList(readOnlyFields));
143            }
144        }
145    
146        /**
147         * @see org.kuali.rice.krad.uif.view.ViewModel#getViewId()
148         */
149        public String getViewId() {
150            return this.viewId;
151        }
152    
153        /**
154         * @see org.kuali.rice.krad.uif.view.ViewModel#setViewId(java.lang.String)
155         */
156        public void setViewId(String viewId) {
157            this.viewId = viewId;
158        }
159    
160        /**
161         * @see org.kuali.rice.krad.uif.view.ViewModel#getViewName()
162         */
163        public String getViewName() {
164            return this.viewName;
165        }
166    
167        /**
168         * @see org.kuali.rice.krad.uif.view.ViewModel#setViewName(java.lang.String)
169         */
170        public void setViewName(String viewName) {
171            this.viewName = viewName;
172        }
173    
174        /**
175         * @see org.kuali.rice.krad.uif.view.ViewModel#getViewTypeName()
176         */
177        public ViewType getViewTypeName() {
178            return this.viewTypeName;
179        }
180    
181        /**
182         * @see org.kuali.rice.krad.uif.view.ViewModel#setViewTypeName(org.kuali.rice.krad.uif.UifConstants.ViewType)
183         */
184        public void setViewTypeName(ViewType viewTypeName) {
185            this.viewTypeName = viewTypeName;
186        }
187    
188        /**
189         * @see org.kuali.rice.krad.uif.view.ViewModel#getPageId()
190         */
191        public String getPageId() {
192            return this.pageId;
193        }
194    
195        /**
196         * @see org.kuali.rice.krad.uif.view.ViewModel#setPageId(java.lang.String)
197         */
198        public void setPageId(String pageId) {
199            this.pageId = pageId;
200        }
201    
202        /**
203         * @see org.kuali.rice.krad.uif.view.ViewModel#getFormPostUrl()
204         */
205        public String getFormPostUrl() {
206            return this.formPostUrl;
207        }
208    
209        /**
210         * @see org.kuali.rice.krad.uif.view.ViewModel#setFormPostUrl(java.lang.String)
211         */
212        public void setFormPostUrl(String formPostUrl) {
213            this.formPostUrl = formPostUrl;
214        }
215    
216        public String getReturnLocation() {
217            return this.returnLocation;
218        }
219    
220        public void setReturnLocation(String returnLocation) {
221            this.returnLocation = returnLocation;
222        }
223    
224        public String getReturnFormKey() {
225            return this.returnFormKey;
226        }
227    
228        public void setReturnFormKey(String returnFormKey) {
229            this.returnFormKey = returnFormKey;
230        }
231    
232        /**
233         * Identifies the controller method that should be invoked to fulfill a
234         * request. The value will be matched up against the 'params' setting on the
235         * <code>RequestMapping</code> annotation for the controller method
236         *
237         * @return String method to call
238         */
239        public String getMethodToCall() {
240            return this.methodToCall;
241        }
242    
243        /**
244         * Setter for the method to call
245         *
246         * @param methodToCall
247         */
248        public void setMethodToCall(String methodToCall) {
249            this.methodToCall = methodToCall;
250        }
251    
252        /**
253         * @see org.kuali.rice.krad.uif.view.ViewModel#getViewRequestParameters()
254         */
255        public Map<String, String> getViewRequestParameters() {
256            return this.viewRequestParameters;
257        }
258    
259        /**
260         * @see org.kuali.rice.krad.uif.view.ViewModel#setViewRequestParameters(java.util.Map<java.lang.String,java.lang.String>)
261         */
262        public void setViewRequestParameters(Map<String, String> viewRequestParameters) {
263            this.viewRequestParameters = viewRequestParameters;
264        }
265    
266        /**
267         * @see org.kuali.rice.krad.uif.view.ViewModel#getReadOnlyFieldsList()
268         */
269        public List<String> getReadOnlyFieldsList() {
270            return readOnlyFieldsList;
271        }
272    
273        /**
274         * @see org.kuali.rice.krad.uif.view.ViewModel#setReadOnlyFieldsList(java.util.List<java.lang.String>)
275         */
276        public void setReadOnlyFieldsList(List<String> readOnlyFieldsList) {
277            this.readOnlyFieldsList = readOnlyFieldsList;
278        }
279    
280        /**
281         * @see org.kuali.rice.krad.uif.view.ViewModel#getNewCollectionLines()
282         */
283        public Map<String, Object> getNewCollectionLines() {
284            return this.newCollectionLines;
285        }
286    
287        /**
288         * @see org.kuali.rice.krad.uif.view.ViewModel#setNewCollectionLines(java.util.Map<java.lang.String,java.lang.Object>)
289         */
290        public void setNewCollectionLines(Map<String, Object> newCollectionLines) {
291            this.newCollectionLines = newCollectionLines;
292        }
293    
294        /**
295         * @see org.kuali.rice.krad.uif.view.ViewModel#getActionParameters()
296         */
297        public Map<String, String> getActionParameters() {
298            return this.actionParameters;
299        }
300    
301        /**
302         * Returns the action parameters map as a <code>Properties</code> instance
303         *
304         * @return Properties action parameters
305         */
306        public Properties getActionParametersAsProperties() {
307            Properties actionProperties = new Properties();
308    
309            if (actionParameters != null) {
310                for (Map.Entry<String, String> actionParameter : actionParameters.entrySet()) {
311                    actionProperties.put(actionParameter.getKey(), actionParameter.getValue());
312                }
313            }
314    
315            return actionProperties;
316        }
317    
318        /**
319         * @see org.kuali.rice.krad.uif.view.ViewModel#setActionParameters(java.util.Map<java.lang.String,java.lang.String>)
320         */
321        public void setActionParameters(Map<String, String> actionParameters) {
322            this.actionParameters = actionParameters;
323        }
324    
325        /**
326         * Retrieves the value for the given action parameter, or empty string if
327         * not found
328         *
329         * @param actionParameterName - name of the action parameter to retrieve value for
330         * @return String parameter value or empty string
331         */
332        public String getActionParamaterValue(String actionParameterName) {
333            if ((actionParameters != null) && actionParameters.containsKey(actionParameterName)) {
334                return actionParameters.get(actionParameterName);
335            }
336    
337            return "";
338        }
339    
340        /**
341         * Returns the action event that was sent in the action parameters (if any)
342         *
343         * <p>
344         * The action event is a special action parameter that can be sent to indicate a type of action being taken. This
345         * can be looked at by the view or components to render differently
346         * </p>
347         *
348         * TODO: make sure action parameters are getting reinitialized on each request
349         *
350         * @return String action event name or blank if action event was not sent
351         */
352        public String getActionEvent() {
353            if ((actionParameters != null) && actionParameters.containsKey(UifConstants.UrlParams.ACTION_EVENT)) {
354                return actionParameters.get(UifConstants.UrlParams.ACTION_EVENT);
355            }
356    
357            return "";
358        }
359    
360        /**
361         * @see org.kuali.rice.krad.uif.view.ViewModel#getClientStateForSyncing()
362         */
363        public Map<String, Object> getClientStateForSyncing() {
364            return clientStateForSyncing;
365        }
366    
367        /**
368         * @see org.kuali.rice.krad.uif.view.ViewModel#getSelectedCollectionLines()
369         */
370        public Map<String, Set<String>> getSelectedCollectionLines() {
371            return selectedCollectionLines;
372        }
373    
374        /**
375         * @see org.kuali.rice.krad.uif.view.ViewModel#setSelectedCollectionLines(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)
376         */
377        public void setSelectedCollectionLines(Map<String, Set<String>> selectedCollectionLines) {
378            this.selectedCollectionLines = selectedCollectionLines;
379        }
380    
381        /**
382         * Key string that identifies the form instance in session storage
383         *
384         * <p>
385         * When the view is posted, the previous form instance is retrieved and then
386         * populated from the request parameters. This key string is retrieve the
387         * session form from the session service
388         * </p>
389         *
390         * @return String form session key
391         */
392        public String getFormKey() {
393            return this.formKey;
394        }
395    
396        /**
397         * Setter for the form's session key
398         *
399         * @param formKey
400         */
401        public void setFormKey(String formKey) {
402            this.formKey = formKey;
403        }
404    
405        /**
406         * @see org.kuali.rice.krad.uif.view.ViewModel#isDefaultsApplied()
407         */
408        public boolean isDefaultsApplied() {
409            return this.defaultsApplied;
410        }
411    
412        /**
413         * @see org.kuali.rice.krad.uif.view.ViewModel#setDefaultsApplied(boolean)
414         */
415        public void setDefaultsApplied(boolean defaultsApplied) {
416            this.defaultsApplied = defaultsApplied;
417        }
418    
419        /**
420         * Holder for files that are attached through the view
421         *
422         * @return MultipartFile representing the attachment
423         */
424        public MultipartFile getAttachmentFile() {
425            return this.attachmentFile;
426        }
427    
428        /**
429         * Setter for the form's attachment file
430         *
431         * @param attachmentFile
432         */
433        public void setAttachmentFile(MultipartFile attachmentFile) {
434            this.attachmentFile = attachmentFile;
435        }
436    
437        /**
438         * @return the renderFullView
439         */
440        public boolean isRenderFullView() {
441            return this.renderFullView;
442        }
443    
444        /**
445         * @param renderFullView
446         */
447        public void setRenderFullView(boolean renderFullView) {
448            this.renderFullView = renderFullView;
449        }
450    
451        /**
452         * @see org.kuali.rice.krad.uif.view.ViewModel#getView()
453         */
454        public View getView() {
455            return this.view;
456        }
457    
458        /**
459         * @see org.kuali.rice.krad.uif.view.ViewModel#setView(org.kuali.rice.krad.uif.view.View)
460         */
461        public void setView(View view) {
462            this.view = view;
463        }
464    
465        /**
466         * @see org.kuali.rice.krad.uif.view.ViewModel#getPreviousView()
467         */
468        public View getPreviousView() {
469            return this.previousView;
470        }
471    
472        /**
473         * @see org.kuali.rice.krad.uif.view.ViewModel#setPreviousView(org.kuali.rice.krad.uif.view.View)
474         */
475        public void setPreviousView(View previousView) {
476            this.previousView = previousView;
477        }
478    
479        /**
480         * Instance of the <code>ViewService</code> that can be used to retrieve
481         * <code>View</code> instances
482         *
483         * @return ViewService implementation
484         */
485        protected ViewService getViewService() {
486            return KRADServiceLocatorWeb.getViewService();
487        }
488    
489        /**
490         * The jumpToId for this form, the element with this id will be jumped to automatically
491         * when the form is loaded in the view.
492         * Using "TOP" or "BOTTOM" will jump to the top or the bottom of the resulting page.
493         * jumpToId always takes precedence over jumpToName, if set.
494         *
495         * @return the jumpToId
496         */
497        public String getJumpToId() {
498            return this.jumpToId;
499        }
500    
501        /**
502         * @param jumpToId the jumpToId to set
503         */
504        public void setJumpToId(String jumpToId) {
505            this.jumpToId = jumpToId;
506        }
507    
508        /**
509         * The jumpToName for this form, the element with this name will be jumped to automatically
510         * when the form is loaded in the view.
511         * WARNING: jumpToId always takes precedence over jumpToName, if set.
512         *
513         * @return the jumpToName
514         */
515        public String getJumpToName() {
516            return this.jumpToName;
517        }
518    
519        /**
520         * @param jumpToName the jumpToName to set
521         */
522        public void setJumpToName(String jumpToName) {
523            this.jumpToName = jumpToName;
524        }
525    
526        /**
527         * Field to place focus on when the page loads
528         * An empty focusId will result in focusing on the first visible input element by default.
529         *
530         * @return the focusId
531         */
532        public String getFocusId() {
533            return this.focusId;
534        }
535    
536        /**
537         * @param focusId the focusId to set
538         */
539        public void setFocusId(String focusId) {
540            this.focusId = focusId;
541        }
542    
543        /**
544         * History parameter representing the History of views that have come before the
545         * viewing of the current view
546         *
547         * <p>
548         * Used for breadcrumb widget generation on the view and also for navigating back
549         * to previous or hub locations
550         * </p>
551         *
552         * @return History instance giving current history
553         */
554        public History getFormHistory() {
555            return formHistory;
556        }
557    
558        /**
559         * Setter for the current History object
560         *
561         * @param history the history to set
562         */
563        public void setFormHistory(History history) {
564            this.formHistory = history;
565        }
566    
567        /**
568         * Indicates whether the form should be validated for dirtyness
569         *
570         * <p>
571         * For FormView, it's necessary to validate when the user tries to navigate out of the form. If set, all the
572         * InputFields will be validated on refresh, navigate, cancel or close Action or on form
573         * unload and if dirty, displays a message and user can decide whether to continue with
574         * the action or stay on the form
575         * </p>
576         *
577         * @return boolean true if dirty validation should be enabled
578         */
579        public boolean isValidateDirty() {
580            return this.validateDirty;
581        }
582    
583        /**
584         * Setter for dirty validation indicator
585         *
586         * @param validateDirty
587         */
588        public void setValidateDirty(boolean validateDirty) {
589            this.validateDirty = validateDirty;
590        }
591    
592    }