View Javadoc

1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.web.form;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.codehaus.jackson.map.ObjectMapper;
22  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
23  import org.kuali.rice.krad.uif.UifConstants;
24  import org.kuali.rice.krad.uif.UifParameters;
25  import org.kuali.rice.krad.uif.view.History;
26  import org.kuali.rice.krad.uif.view.HistoryEntry;
27  import org.kuali.rice.krad.uif.view.View;
28  import org.kuali.rice.krad.uif.service.ViewService;
29  import org.kuali.rice.krad.uif.view.ViewModel;
30  import org.kuali.rice.krad.util.KRADUtils;
31  import org.springframework.web.multipart.MultipartFile;
32  import org.kuali.rice.krad.uif.UifConstants.ViewType;
33  
34  import javax.servlet.http.HttpServletRequest;
35  import java.io.IOException;
36  import java.util.ArrayList;
37  import java.util.HashMap;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.Properties;
41  import java.util.Set;
42  import java.util.UUID;
43  
44  /**
45   * Base form class for views within the KRAD User Interface Framework
46   *
47   * <p>
48   * Holds properties necessary to determine the <code>View</code> instance that
49   * will be used to render the UI
50   * </p>
51   *
52   * @author Kuali Rice Team (rice.collab@kuali.org)
53   */
54  public class UifFormBase implements ViewModel {
55      private static final long serialVersionUID = 8432543267099454434L;
56  
57      // logger
58      private static final Log LOG = LogFactory.getLog(UifFormBase.class);
59  
60      // current view
61      protected String viewId;
62      protected String viewName;
63      protected ViewType viewTypeName;
64      protected String pageId;
65      protected String methodToCall;
66      protected String formKey;
67      protected String jumpToId;
68      protected String jumpToName;
69      protected String focusId;
70      protected String formPostUrl;
71  
72      protected boolean defaultsApplied;
73      protected boolean skipViewInit;
74  
75      protected View view;
76      protected View postedView;
77  
78      protected Map<String, String> viewRequestParameters;
79      protected List<String> readOnlyFieldsList;
80  
81      protected Map<String, Object> newCollectionLines;
82      protected Map<String, String> actionParameters;
83      protected Map<String, Object> clientStateForSyncing;
84      protected Map<String, Set<String>> selectedCollectionLines;
85  
86      protected MultipartFile attachmentFile;
87  
88      // navigation
89      protected String returnLocation;
90      protected String returnFormKey;
91  
92      protected History formHistory;
93  
94      protected boolean renderFullView;
95      protected boolean validateDirty;
96  
97      public UifFormBase() {
98          formKey = generateFormKey();
99          renderFullView = true;
100         defaultsApplied = false;
101         skipViewInit = false;
102 
103         formHistory = new History();
104 
105         readOnlyFieldsList = new ArrayList<String>();
106         viewRequestParameters = new HashMap<String, String>();
107         newCollectionLines = new HashMap<String, Object>();
108         actionParameters = new HashMap<String, String>();
109         clientStateForSyncing = new HashMap<String, Object>();
110         selectedCollectionLines = new HashMap<String, Set<String>>();
111     }
112 
113     /**
114      * Creates the unique id used to store this "conversation" in the session.
115      * The default method generates a java UUID.
116      *
117      * @return
118      */
119     protected String generateFormKey() {
120         return UUID.randomUUID().toString();
121     }
122 
123     /**
124      * Called after Spring binds the request to the form and before the
125      * controller method is invoked.
126      *
127      * @param request - request object containing the query parameters
128      */
129     public void postBind(HttpServletRequest request) {
130         // default form post URL to request URL
131         formPostUrl = request.getRequestURL().toString();
132 
133         // get any sent client view state and parse into map
134         if (request.getParameterMap().containsKey(UifParameters.CLIENT_VIEW_STATE)) {
135             String clientStateJSON = request.getParameter(UifParameters.CLIENT_VIEW_STATE);
136             if (StringUtils.isNotBlank(clientStateJSON)) {
137                 // change single quotes to double quotes (necessary because the reverse was done for sending)
138                 clientStateJSON = StringUtils.replace(clientStateJSON, "'", "\"");
139 
140                 ObjectMapper mapper = new ObjectMapper();
141                 try {
142                     clientStateForSyncing = mapper.readValue(clientStateJSON, Map.class);
143                 } catch (IOException e) {
144                     throw new RuntimeException("Unable to decode client side state JSON", e);
145                 }
146             }
147         }
148 
149         // populate read only fields list
150         if (request.getParameter(UifParameters.READ_ONLY_FIELDS) != null) {
151             String readOnlyFields = request.getParameter(UifParameters.READ_ONLY_FIELDS);
152             setReadOnlyFieldsList(KRADUtils.convertStringParameterToList(readOnlyFields));
153         }
154 
155         // reset skip view init parameter if not passed
156         if (!request.getParameterMap().containsKey(UifParameters.SKIP_VIEW_INIT)) {
157             skipViewInit = false;
158         }
159     }
160 
161     /**
162      * @see org.kuali.rice.krad.uif.view.ViewModel#getViewId()
163      */
164     public String getViewId() {
165         return this.viewId;
166     }
167 
168     /**
169      * @see org.kuali.rice.krad.uif.view.ViewModel#setViewId(java.lang.String)
170      */
171     public void setViewId(String viewId) {
172         this.viewId = viewId;
173     }
174 
175     /**
176      * @see org.kuali.rice.krad.uif.view.ViewModel#getViewName()
177      */
178     public String getViewName() {
179         return this.viewName;
180     }
181 
182     /**
183      * @see org.kuali.rice.krad.uif.view.ViewModel#setViewName(java.lang.String)
184      */
185     public void setViewName(String viewName) {
186         this.viewName = viewName;
187     }
188 
189     /**
190      * @see org.kuali.rice.krad.uif.view.ViewModel#getViewTypeName()
191      */
192     public ViewType getViewTypeName() {
193         return this.viewTypeName;
194     }
195 
196     /**
197      * @see org.kuali.rice.krad.uif.view.ViewModel#setViewTypeName(org.kuali.rice.krad.uif.UifConstants.ViewType)
198      */
199     public void setViewTypeName(ViewType viewTypeName) {
200         this.viewTypeName = viewTypeName;
201     }
202 
203     /**
204      * @see org.kuali.rice.krad.uif.view.ViewModel#getPageId()
205      */
206     public String getPageId() {
207         return this.pageId;
208     }
209 
210     /**
211      * @see org.kuali.rice.krad.uif.view.ViewModel#setPageId(java.lang.String)
212      */
213     public void setPageId(String pageId) {
214         this.pageId = pageId;
215     }
216 
217     /**
218      * @see org.kuali.rice.krad.uif.view.ViewModel#getFormPostUrl()
219      */
220     public String getFormPostUrl() {
221         return this.formPostUrl;
222     }
223 
224     /**
225      * @see org.kuali.rice.krad.uif.view.ViewModel#setFormPostUrl(java.lang.String)
226      */
227     public void setFormPostUrl(String formPostUrl) {
228         this.formPostUrl = formPostUrl;
229     }
230 
231     public String getReturnLocation() {
232         return this.returnLocation;
233     }
234 
235     public void setReturnLocation(String returnLocation) {
236         this.returnLocation = returnLocation;
237     }
238 
239     public String getReturnFormKey() {
240         return this.returnFormKey;
241     }
242 
243     public void setReturnFormKey(String returnFormKey) {
244         this.returnFormKey = returnFormKey;
245     }
246 
247     /**
248      * Identifies the controller method that should be invoked to fulfill a
249      * request. The value will be matched up against the 'params' setting on the
250      * <code>RequestMapping</code> annotation for the controller method
251      *
252      * @return String method to call
253      */
254     public String getMethodToCall() {
255         return this.methodToCall;
256     }
257 
258     /**
259      * Setter for the method to call
260      *
261      * @param methodToCall
262      */
263     public void setMethodToCall(String methodToCall) {
264         this.methodToCall = methodToCall;
265     }
266 
267     /**
268      * @see org.kuali.rice.krad.uif.view.ViewModel#getViewRequestParameters()
269      */
270     public Map<String, String> getViewRequestParameters() {
271         return this.viewRequestParameters;
272     }
273 
274     /**
275      * @see org.kuali.rice.krad.uif.view.ViewModel#setViewRequestParameters(java.util.Map<java.lang.String,java.lang.String>)
276      */
277     public void setViewRequestParameters(Map<String, String> viewRequestParameters) {
278         this.viewRequestParameters = viewRequestParameters;
279     }
280 
281     /**
282      * @see org.kuali.rice.krad.uif.view.ViewModel#getReadOnlyFieldsList()
283      */
284     public List<String> getReadOnlyFieldsList() {
285         return readOnlyFieldsList;
286     }
287 
288     /**
289      * @see org.kuali.rice.krad.uif.view.ViewModel#setReadOnlyFieldsList(java.util.List<java.lang.String>)
290      */
291     public void setReadOnlyFieldsList(List<String> readOnlyFieldsList) {
292         this.readOnlyFieldsList = readOnlyFieldsList;
293     }
294 
295     /**
296      * @see org.kuali.rice.krad.uif.view.ViewModel#getNewCollectionLines()
297      */
298     public Map<String, Object> getNewCollectionLines() {
299         return this.newCollectionLines;
300     }
301 
302     /**
303      * @see org.kuali.rice.krad.uif.view.ViewModel#setNewCollectionLines(java.util.Map<java.lang.String,java.lang.Object>)
304      */
305     public void setNewCollectionLines(Map<String, Object> newCollectionLines) {
306         this.newCollectionLines = newCollectionLines;
307     }
308 
309     /**
310      * @see org.kuali.rice.krad.uif.view.ViewModel#getActionParameters()
311      */
312     public Map<String, String> getActionParameters() {
313         return this.actionParameters;
314     }
315 
316     /**
317      * Returns the action parameters map as a <code>Properties</code> instance
318      *
319      * @return Properties action parameters
320      */
321     public Properties getActionParametersAsProperties() {
322         return KRADUtils.convertMapToProperties(actionParameters);
323     }
324 
325     /**
326      * @see org.kuali.rice.krad.uif.view.ViewModel#setActionParameters(java.util.Map<java.lang.String,java.lang.String>)
327      */
328     public void setActionParameters(Map<String, String> actionParameters) {
329         this.actionParameters = actionParameters;
330     }
331 
332     /**
333      * Retrieves the value for the given action parameter, or empty string if
334      * not found
335      *
336      * @param actionParameterName - name of the action parameter to retrieve value for
337      * @return String parameter value or empty string
338      */
339     public String getActionParamaterValue(String actionParameterName) {
340         if ((actionParameters != null) && actionParameters.containsKey(actionParameterName)) {
341             return actionParameters.get(actionParameterName);
342         }
343 
344         return "";
345     }
346 
347     /**
348      * Returns the action event that was sent in the action parameters (if any)
349      *
350      * <p>
351      * The action event is a special action parameter that can be sent to indicate a type of action being taken. This
352      * can be looked at by the view or components to render differently
353      * </p>
354      *
355      * TODO: make sure action parameters are getting reinitialized on each request
356      *
357      * @return String action event name or blank if action event was not sent
358      */
359     public String getActionEvent() {
360         if ((actionParameters != null) && actionParameters.containsKey(UifConstants.UrlParams.ACTION_EVENT)) {
361             return actionParameters.get(UifConstants.UrlParams.ACTION_EVENT);
362         }
363 
364         return "";
365     }
366 
367     /**
368      * @see org.kuali.rice.krad.uif.view.ViewModel#getClientStateForSyncing()
369      */
370     public Map<String, Object> getClientStateForSyncing() {
371         return clientStateForSyncing;
372     }
373 
374     /**
375      * @see org.kuali.rice.krad.uif.view.ViewModel#getSelectedCollectionLines()
376      */
377     public Map<String, Set<String>> getSelectedCollectionLines() {
378         return selectedCollectionLines;
379     }
380 
381     /**
382      * @see org.kuali.rice.krad.uif.view.ViewModel#setSelectedCollectionLines(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)
383      */
384     public void setSelectedCollectionLines(Map<String, Set<String>> selectedCollectionLines) {
385         this.selectedCollectionLines = selectedCollectionLines;
386     }
387 
388     /**
389      * Key string that identifies the form instance in session storage
390      *
391      * <p>
392      * When the view is posted, the previous form instance is retrieved and then
393      * populated from the request parameters. This key string is retrieve the
394      * session form from the session service
395      * </p>
396      *
397      * @return String form session key
398      */
399     public String getFormKey() {
400         return this.formKey;
401     }
402 
403     /**
404      * Setter for the form's session key
405      *
406      * @param formKey
407      */
408     public void setFormKey(String formKey) {
409         this.formKey = formKey;
410     }
411 
412     /**
413      * @see org.kuali.rice.krad.uif.view.ViewModel#isDefaultsApplied()
414      */
415     public boolean isDefaultsApplied() {
416         return this.defaultsApplied;
417     }
418 
419     /**
420      * @see org.kuali.rice.krad.uif.view.ViewModel#setDefaultsApplied(boolean)
421      */
422     public void setDefaultsApplied(boolean defaultsApplied) {
423         this.defaultsApplied = defaultsApplied;
424     }
425 
426     /**
427      * Indicates whether a new view is being initialized or the call is refresh (or query) call
428      *
429      * @return boolean true if view initialization was skipped, false if new view is being created
430      */
431     public boolean isSkipViewInit() {
432         return skipViewInit;
433     }
434 
435     /**
436      * Setter for the skip view initialization flag
437      *
438      * @param skipViewInit
439      */
440     public void setSkipViewInit(boolean skipViewInit) {
441         this.skipViewInit = skipViewInit;
442     }
443 
444     /**
445      * Holder for files that are attached through the view
446      *
447      * @return MultipartFile representing the attachment
448      */
449     public MultipartFile getAttachmentFile() {
450         return this.attachmentFile;
451     }
452 
453     /**
454      * Setter for the form's attachment file
455      *
456      * @param attachmentFile
457      */
458     public void setAttachmentFile(MultipartFile attachmentFile) {
459         this.attachmentFile = attachmentFile;
460     }
461 
462     /**
463      * @return the renderFullView
464      */
465     public boolean isRenderFullView() {
466         return this.renderFullView;
467     }
468 
469     /**
470      * @param renderFullView
471      */
472     public void setRenderFullView(boolean renderFullView) {
473         this.renderFullView = renderFullView;
474     }
475 
476     /**
477      * @see org.kuali.rice.krad.uif.view.ViewModel#getView()
478      */
479     public View getView() {
480         return this.view;
481     }
482 
483     /**
484      * @see org.kuali.rice.krad.uif.view.ViewModel#setView(org.kuali.rice.krad.uif.view.View)
485      */
486     public void setView(View view) {
487         this.view = view;
488         initHomewardPathList();
489     }
490 
491     /**
492      * Set the "Home" url of the homewardPathList (ie. breadcrumbs history)
493      */
494     private void initHomewardPathList() {
495         if (getReturnLocation() == null) {
496             LOG.warn("Could not init homewardPathList.  returnLocation is null.");
497             return;
498         }
499 
500         List<HistoryEntry> homewardPathList = new ArrayList<HistoryEntry>();
501         if ((view != null) && (view.getBreadcrumbs() != null) && (view.getBreadcrumbs().getHomewardPathList() != null)) {
502             homewardPathList = view.getBreadcrumbs().getHomewardPathList();
503         }
504 
505         HistoryEntry historyEntry = new HistoryEntry("","","Home",getReturnLocation(),"");
506         if (homewardPathList.isEmpty()) {
507             homewardPathList.add(historyEntry);
508         } else if (StringUtils.equals(homewardPathList.get(0).getTitle(), "Home")) {
509             homewardPathList.set(0, historyEntry);
510         } else {
511             homewardPathList.add(0, historyEntry);
512         }
513     }
514 
515     /**
516      * @see org.kuali.rice.krad.uif.view.ViewModel#getPostedView()
517      */
518     public View getPostedView() {
519         return this.postedView;
520     }
521 
522     /**
523      * @see org.kuali.rice.krad.uif.view.ViewModel#setPostedView(org.kuali.rice.krad.uif.view.View)
524      */
525     public void setPostedView(View postedView) {
526         this.postedView = postedView;
527     }
528 
529     /**
530      * Instance of the <code>ViewService</code> that can be used to retrieve
531      * <code>View</code> instances
532      *
533      * @return ViewService implementation
534      */
535     protected ViewService getViewService() {
536         return KRADServiceLocatorWeb.getViewService();
537     }
538 
539     /**
540      * The jumpToId for this form, the element with this id will be jumped to automatically
541      * when the form is loaded in the view.
542      * Using "TOP" or "BOTTOM" will jump to the top or the bottom of the resulting page.
543      * jumpToId always takes precedence over jumpToName, if set.
544      *
545      * @return the jumpToId
546      */
547     public String getJumpToId() {
548         return this.jumpToId;
549     }
550 
551     /**
552      * @param jumpToId the jumpToId to set
553      */
554     public void setJumpToId(String jumpToId) {
555         this.jumpToId = jumpToId;
556     }
557 
558     /**
559      * The jumpToName for this form, the element with this name will be jumped to automatically
560      * when the form is loaded in the view.
561      * WARNING: jumpToId always takes precedence over jumpToName, if set.
562      *
563      * @return the jumpToName
564      */
565     public String getJumpToName() {
566         return this.jumpToName;
567     }
568 
569     /**
570      * @param jumpToName the jumpToName to set
571      */
572     public void setJumpToName(String jumpToName) {
573         this.jumpToName = jumpToName;
574     }
575 
576     /**
577      * Field to place focus on when the page loads
578      * An empty focusId will result in focusing on the first visible input element by default.
579      *
580      * @return the focusId
581      */
582     public String getFocusId() {
583         return this.focusId;
584     }
585 
586     /**
587      * @param focusId the focusId to set
588      */
589     public void setFocusId(String focusId) {
590         this.focusId = focusId;
591     }
592 
593     /**
594      * History parameter representing the History of views that have come before the
595      * viewing of the current view
596      *
597      * <p>
598      * Used for breadcrumb widget generation on the view and also for navigating back
599      * to previous or hub locations
600      * </p>
601      *
602      * @return History instance giving current history
603      */
604     public History getFormHistory() {
605         return formHistory;
606     }
607 
608     /**
609      * Setter for the current History object
610      *
611      * @param history the history to set
612      */
613     public void setFormHistory(History history) {
614         this.formHistory = history;
615     }
616 
617     /**
618      * Indicates whether the form should be validated for dirtyness
619      *
620      * <p>
621      * For FormView, it's necessary to validate when the user tries to navigate out of the form. If set, all the
622      * InputFields will be validated on refresh, navigate, cancel or close Action or on form
623      * unload and if dirty, displays a message and user can decide whether to continue with
624      * the action or stay on the form
625      * </p>
626      *
627      * @return boolean true if dirty validation should be enabled
628      */
629     public boolean isValidateDirty() {
630         return this.validateDirty;
631     }
632 
633     /**
634      * Setter for dirty validation indicator
635      *
636      * @param validateDirty
637      */
638     public void setValidateDirty(boolean validateDirty) {
639         this.validateDirty = validateDirty;
640     }
641 
642 }