View Javadoc
1   /**
2    * Copyright 2005-2016 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.codehaus.jackson.map.ObjectMapper;
20  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
21  import org.kuali.rice.krad.uif.UifConstants;
22  import org.kuali.rice.krad.uif.UifConstants.ViewType;
23  import org.kuali.rice.krad.uif.UifParameters;
24  import org.kuali.rice.krad.uif.UifPropertyPaths;
25  import org.kuali.rice.krad.uif.component.Component;
26  import org.kuali.rice.krad.uif.lifecycle.ViewPostMetadata;
27  import org.kuali.rice.krad.uif.service.ViewHelperService;
28  import org.kuali.rice.krad.uif.service.ViewService;
29  import org.kuali.rice.krad.uif.util.SessionTransient;
30  import org.kuali.rice.krad.uif.view.View;
31  import org.kuali.rice.krad.uif.view.ViewModel;
32  import org.kuali.rice.krad.util.CsrfValidator;
33  import org.kuali.rice.krad.util.KRADUtils;
34  import org.kuali.rice.krad.web.bind.RequestAccessible;
35  import org.springframework.web.multipart.MultipartFile;
36  
37  import javax.servlet.http.HttpServletRequest;
38  import java.io.IOException;
39  import java.util.ArrayList;
40  import java.util.Enumeration;
41  import java.util.HashMap;
42  import java.util.HashSet;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.Properties;
46  import java.util.Set;
47  import java.util.UUID;
48  
49  /**
50   * Base form class for views within the KRAD User Interface Framework.
51   *
52   * <p>Holds properties necessary to determine the {@link org.kuali.rice.krad.uif.view.View} instance that
53   * will be used to render the user interface</p>
54   *
55   * @author Kuali Rice Team (rice.collab@kuali.org)
56   */
57  public class UifFormBase implements ViewModel {
58  
59      private static final long serialVersionUID = 8432543267099454434L;
60  
61      @RequestAccessible
62      protected String viewId;
63  
64      @RequestAccessible
65      protected String viewName;
66  
67      @RequestAccessible
68      protected ViewType viewTypeName;
69  
70      @RequestAccessible
71      protected String pageId;
72  
73      @RequestAccessible
74      protected String methodToCall;
75  
76      @RequestAccessible
77      protected String formKey;
78  
79      @RequestAccessible
80      @SessionTransient
81      protected String requestedFormKey;
82  
83      @RequestAccessible
84      protected String flowKey;
85  
86      protected String sessionId;
87      protected int sessionTimeoutInterval;
88  
89      @SessionTransient
90      protected HistoryFlow historyFlow;
91      @SessionTransient
92      protected HistoryManager historyManager;
93  
94      @RequestAccessible
95      @SessionTransient
96      protected String jumpToId;
97  
98      @SessionTransient
99      protected String jumpToName;
100 
101     @RequestAccessible
102     @SessionTransient
103     protected String focusId;
104 
105     @RequestAccessible
106     @SessionTransient
107     protected boolean dirtyForm;
108 
109     protected String formPostUrl;
110     protected String controllerMapping;
111 
112     @SessionTransient
113     private String requestUrl;
114     private Map<String, String[]> initialRequestParameters;
115 
116     protected String state;
117 
118     @RequestAccessible
119     protected boolean renderedInDialog;
120 
121     @RequestAccessible
122     protected boolean renderedInIframe;
123 
124     @SessionTransient
125     protected String growlScript;
126 
127     @SessionTransient
128     protected View view;
129     protected ViewPostMetadata viewPostMetadata;
130 
131     protected Map<String, String> viewRequestParameters;
132     protected List<String> readOnlyFieldsList;
133 
134     protected Map<String, Object> newCollectionLines;
135 
136     @RequestAccessible
137     @SessionTransient
138     protected String triggerActionId;
139 
140     @RequestAccessible
141     @SessionTransient
142     protected Map<String, String> actionParameters;
143 
144     protected Map<String, Object> clientStateForSyncing;
145 
146     @SessionTransient
147     protected Map<String, Set<String>> selectedCollectionLines;
148 
149     protected Set<String> selectedLookupResultsCache;
150 
151     protected List<Object> addedCollectionItems;
152 
153     @SessionTransient
154     protected MultipartFile attachmentFile;
155 
156     // navigation
157     @RequestAccessible
158     protected String returnLocation;
159 
160     @RequestAccessible
161     protected String returnFormKey;
162 
163     @RequestAccessible
164     @SessionTransient
165     protected boolean ajaxRequest;
166 
167     @RequestAccessible
168     @SessionTransient
169     protected String ajaxReturnType;
170 
171     @SessionTransient
172     private String requestJsonTemplate;
173     @SessionTransient
174     private boolean collectionPagingRequest;
175 
176     // dialog fields
177     @RequestAccessible
178     @SessionTransient
179     protected String showDialogId;
180 
181     @RequestAccessible
182     @SessionTransient
183     protected String returnDialogId;
184 
185     @RequestAccessible
186     @SessionTransient
187     protected String returnDialogResponse;
188 
189     @RequestAccessible
190     protected Map<String, String> dialogExplanations;
191     protected Map<String, DialogResponse> dialogResponses;
192 
193     @SessionTransient
194     protected boolean requestRedirected;
195 
196     @RequestAccessible
197     @SessionTransient
198     protected String updateComponentId;
199     @SessionTransient
200     private Component updateComponent;
201 
202     @RequestAccessible
203     protected Map<String, Object> extensionData;
204 
205     protected boolean applyDefaultValues;
206 
207     protected boolean evaluateFlagsAndModes;
208     protected Boolean canEditView;
209     protected Map<String, Boolean> actionFlags;
210     protected Map<String, Boolean> editModes;
211 
212     protected String csrfToken;
213 
214     protected HttpServletRequest request;
215 
216     private Object dialogDataObject;
217 
218     public UifFormBase() {
219         renderedInDialog = false;
220         renderedInIframe = false;
221         requestRedirected = false;
222 
223         readOnlyFieldsList = new ArrayList<String>();
224         viewRequestParameters = new HashMap<String, String>();
225         newCollectionLines = new HashMap<String, Object>();
226         actionParameters = new HashMap<String, String>();
227         clientStateForSyncing = new HashMap<String, Object>();
228         selectedCollectionLines = new HashMap<String, Set<String>>();
229         selectedLookupResultsCache = new HashSet<String>();
230         addedCollectionItems = new ArrayList<Object>();
231         dialogExplanations = new HashMap<String, String>();
232         dialogResponses = new HashMap<String, DialogResponse>();
233         extensionData = new HashMap<String, Object>();
234 
235         applyDefaultValues = true;
236         evaluateFlagsAndModes = true;
237     }
238 
239     /**
240      * {@inheritDoc}
241      */
242     @Override
243     public void preBind(HttpServletRequest request) {
244         String formKeyParam = request.getParameter(UifParameters.FORM_KEY);
245         if (StringUtils.isNotBlank(formKeyParam)) {
246             UifFormManager uifFormManager = (UifFormManager) request.getSession().getAttribute(
247                     UifParameters.FORM_MANAGER);
248 
249             // retrieves the session form and updates the request from with the session transient attributes
250             uifFormManager.updateFormWithSession(this, formKeyParam);
251         }
252 
253         String requestedFormKey = request.getParameter(UifParameters.REQUESTED_FORM_KEY);
254         if (StringUtils.isNotBlank(requestedFormKey)) {
255             setRequestedFormKey(requestedFormKey);
256         } else {
257             setRequestedFormKey(formKeyParam);
258         }
259 
260         String csrfToken = CsrfValidator.getSessionToken(request);
261         setCsrfToken(csrfToken);
262 
263         this.request = request;
264     }
265 
266     /**
267      * {@inheritDoc}
268      */
269     @Override
270     public void postBind(HttpServletRequest request) {
271         // assign form key if this is a new form or the requested form key is not in session
272         UifFormManager uifFormManager = (UifFormManager) request.getSession().getAttribute(UifParameters.FORM_MANAGER);
273         if (StringUtils.isBlank(formKey) || !uifFormManager.hasSessionForm(formKey)) {
274             formKey = generateFormKey();
275         }
276 
277         // default form post URL to request URL
278         formPostUrl = request.getRequestURL().toString();
279 
280         controllerMapping = request.getPathInfo();
281 
282         if (request.getSession() != null) {
283             sessionId = request.getSession().getId();
284             sessionTimeoutInterval = request.getSession().getMaxInactiveInterval();
285         }
286 
287         // get any sent client view state and parse into map
288         if (request.getParameterMap().containsKey(UifParameters.CLIENT_VIEW_STATE)) {
289             String clientStateJSON = request.getParameter(UifParameters.CLIENT_VIEW_STATE);
290             if (StringUtils.isNotBlank(clientStateJSON)) {
291                 // change single quotes to double quotes (necessary because the reverse was done for sending)
292                 clientStateJSON = StringUtils.replace(clientStateJSON, "\\'", "\"");
293                 clientStateJSON = StringUtils.replace(clientStateJSON, "\\[", "[");
294                 clientStateJSON = StringUtils.replace(clientStateJSON, "\\]", "]");
295                 clientStateJSON = StringUtils.replace(clientStateJSON, "'", "\"");
296 
297                 ObjectMapper mapper = new ObjectMapper();
298                 try {
299                     clientStateForSyncing = mapper.readValue(clientStateJSON, Map.class);
300                 } catch (IOException e) {
301                     throw new RuntimeException("Unable to decode client side state JSON: " + clientStateJSON, e);
302                 }
303             }
304         }
305 
306         String requestUrl = KRADUtils.stripXSSPatterns(KRADUtils.getFullURL(request));
307         setRequestUrl(requestUrl);
308 
309         String referer = request.getHeader(UifConstants.REFERER);
310         if (StringUtils.isBlank(referer) && StringUtils.isBlank(getReturnLocation())) {
311             setReturnLocation(UifConstants.NO_RETURN);
312         } else if (StringUtils.isBlank(getReturnLocation())) {
313             setReturnLocation(referer);
314         }
315 
316         if (getInitialRequestParameters() == null) {
317             Map<String, String[]> requestParams = new HashMap<String, String[]>();
318             Enumeration<String> names = request.getParameterNames();
319 
320             while (names != null && names.hasMoreElements()) {
321                 String name = KRADUtils.stripXSSPatterns(names.nextElement());
322                 String[] values = KRADUtils.stripXSSPatterns(request.getParameterValues(name));
323 
324                 requestParams.put(name, values);
325             }
326 
327             requestParams.remove(UifConstants.UrlParams.LOGIN_USER);
328             setInitialRequestParameters(requestParams);
329         }
330 
331         // populate read only fields list
332         if (request.getParameter(UifParameters.READ_ONLY_FIELDS) != null) {
333             String readOnlyFields = request.getParameter(UifParameters.READ_ONLY_FIELDS);
334             setReadOnlyFieldsList(KRADUtils.convertStringParameterToList(readOnlyFields));
335         }
336 
337         // collect dialog response, or initialize new map of responses
338         if (request.getParameter(UifParameters.RETURN_FROM_DIALOG) != null) {
339             String dialogExplanation = null;
340             if ((dialogExplanations != null) && dialogExplanations.containsKey(returnDialogId)) {
341                 dialogExplanation = dialogExplanations.get(returnDialogId);
342             }
343 
344             DialogResponse response = new DialogResponse(returnDialogId, returnDialogResponse, dialogExplanation);
345             this.dialogResponses.put(this.returnDialogId, response);
346         } else {
347             this.dialogResponses = new HashMap<String, DialogResponse>();
348         }
349 
350         Object historyManager = request.getSession().getAttribute(UifConstants.HistoryFlow.HISTORY_MANAGER);
351         if (historyManager != null && historyManager instanceof HistoryManager) {
352             setHistoryManager((HistoryManager) historyManager);
353 
354             String flowKey = request.getParameter(UifConstants.HistoryFlow.FLOW);
355             setFlowKey(flowKey);
356         }
357 
358         // clean parameters from XSS attacks that will be written out as hiddens
359         this.pageId = KRADUtils.stripXSSPatterns(this.pageId);
360         this.methodToCall = KRADUtils.stripXSSPatterns(this.methodToCall);
361         this.formKey = KRADUtils.stripXSSPatterns(this.formKey);
362         this.requestedFormKey = KRADUtils.stripXSSPatterns(this.requestedFormKey);
363         this.flowKey = KRADUtils.stripXSSPatterns(this.flowKey);
364         this.sessionId = KRADUtils.stripXSSPatterns(this.sessionId);
365         this.formPostUrl = KRADUtils.stripXSSPatterns(this.formPostUrl);
366         this.returnLocation = KRADUtils.stripXSSPatterns(this.returnLocation);
367         this.returnFormKey = KRADUtils.stripXSSPatterns(this.returnFormKey);
368         this.requestUrl = KRADUtils.stripXSSPatterns(this.requestUrl);
369     }
370 
371     /**
372      * {@inheritDoc}
373      */
374     @Override
375     public void preRender(HttpServletRequest request) {
376         // clear dialog properties so previous values do not appear for new dialogs
377         this.returnDialogId = null;
378         this.returnDialogResponse = null;
379         this.dialogExplanations = new HashMap<String, String>();
380     }
381 
382     /**
383      * Creates the unique id used to store this "conversation" in the session.
384      * The default method generates a java UUID.
385      *
386      * @return UUID
387      */
388     protected String generateFormKey() {
389         return UUID.randomUUID().toString();
390     }
391 
392     /**
393      * @see org.kuali.rice.krad.uif.view.ViewModel#getViewId()
394      */
395     @Override
396     public String getViewId() {
397         return this.viewId;
398     }
399 
400     /**
401      * @see org.kuali.rice.krad.uif.view.ViewModel#setViewId(String)
402      */
403     @Override
404     public void setViewId(String viewId) {
405         this.viewId = viewId;
406     }
407 
408     /**
409      * @see org.kuali.rice.krad.uif.view.ViewModel#getViewName()
410      */
411     @Override
412     public String getViewName() {
413         return this.viewName;
414     }
415 
416     /**
417      * @see org.kuali.rice.krad.uif.view.ViewModel#setViewName(String)
418      */
419     @Override
420     public void setViewName(String viewName) {
421         this.viewName = viewName;
422     }
423 
424     /**
425      * @see org.kuali.rice.krad.uif.view.ViewModel#getViewTypeName()
426      */
427     @Override
428     public ViewType getViewTypeName() {
429         return this.viewTypeName;
430     }
431 
432     /**
433      * @see org.kuali.rice.krad.uif.view.ViewModel#setViewTypeName(org.kuali.rice.krad.uif.UifConstants.ViewType)
434      */
435     @Override
436     public void setViewTypeName(ViewType viewTypeName) {
437         this.viewTypeName = viewTypeName;
438     }
439 
440     /**
441      * @see org.kuali.rice.krad.uif.view.ViewModel#getPageId()
442      */
443     @Override
444     public String getPageId() {
445         return this.pageId;
446     }
447 
448     /**
449      * @see org.kuali.rice.krad.uif.view.ViewModel#setPageId(String)
450      */
451     @Override
452     public void setPageId(String pageId) {
453         this.pageId = pageId;
454     }
455 
456     /**
457      * @see org.kuali.rice.krad.uif.view.ViewModel#getFormPostUrl()
458      */
459     @Override
460     public String getFormPostUrl() {
461         return this.formPostUrl;
462     }
463 
464     /**
465      * @see org.kuali.rice.krad.uif.view.ViewModel#setFormPostUrl(String)
466      */
467     @Override
468     public void setFormPostUrl(String formPostUrl) {
469         this.formPostUrl = formPostUrl;
470     }
471 
472     /**
473      * Name of the controllerMapping for this form (includes slash)
474      *
475      * @return the controllerMapping string
476      */
477     public String getControllerMapping() {
478         return controllerMapping;
479     }
480 
481     /**
482      * The current {@link HistoryFlow} for this form which stores a trail of urls/breadcrumbs primarily used for
483      * path-based breadcrumb display
484      *
485      * @return the {@link HistoryFlow}
486      */
487     public HistoryFlow getHistoryFlow() {
488         return historyFlow;
489     }
490 
491     /**
492      * Set the current HistoryFlow for this form
493      */
494     public void setHistoryFlow(HistoryFlow historyFlow) {
495         this.historyFlow = historyFlow;
496     }
497 
498     /**
499      * The current {@link HistoryManager} that was pulled from session which store all {@link HistoryFlow} objects in
500      * the current session to keep track of the path the user has taken across views (primarily used by path-based
501      * breadcrumbs)
502      *
503      * @return the HistoryManager
504      */
505     public HistoryManager getHistoryManager() {
506         return historyManager;
507     }
508 
509     /**
510      * Set the current HistoryManager
511      */
512     public void setHistoryManager(HistoryManager historyManager) {
513         this.historyManager = historyManager;
514     }
515 
516     /**
517      * The flowKey representing the HistoryFlow this form may be in.
518      *
519      * <p>This allows for a flow to continue by key or start (if set to "start").
520      * If null or blank, no flow (or path based
521      * breadcrumbs) are being tracked.</p>
522      *
523      * @return the flowKey
524      */
525     public String getFlowKey() {
526         return flowKey;
527     }
528 
529     /**
530      * Set the flowKey
531      */
532     public void setFlowKey(String flowKey) {
533         this.flowKey = flowKey;
534     }
535 
536     /**
537      * The original requestUrl for the View represented by this form (url received by the controller for initial
538      * request)
539      *
540      * @return the requestUrl
541      */
542     public String getRequestUrl() {
543         return requestUrl;
544     }
545 
546     /**
547      * Set the requestUrl
548      */
549     public void setRequestUrl(String requestUrl) {
550         this.requestUrl = requestUrl;
551     }
552 
553     /**
554      * The requestParameters represent all the parameters in the query string that were initially passed to this View
555      * by the initial request
556      *
557      * @return the requestParameters
558      */
559     public Map<String, String[]> getInitialRequestParameters() {
560         return initialRequestParameters;
561     }
562 
563     /**
564      * Set the requestParameters
565      */
566     public void setInitialRequestParameters(Map<String, String[]> requestParameters) {
567         this.initialRequestParameters = requestParameters;
568     }
569 
570     public String getReturnLocation() {
571         return this.returnLocation;
572     }
573 
574     public void setReturnLocation(String returnLocation) {
575         this.returnLocation = returnLocation;
576     }
577 
578     public String getReturnFormKey() {
579         return this.returnFormKey;
580     }
581 
582     public void setReturnFormKey(String returnFormKey) {
583         this.returnFormKey = returnFormKey;
584     }
585 
586     /**
587      * Holds the id for the user's current session
588      *
589      * <p>
590      * The user's session id is used to track when a timeout has occurred and enforce the policy
591      * configured with the {@link org.kuali.rice.krad.uif.view.ViewSessionPolicy}. This property gets initialized
592      * in the {@link #postBind(javax.servlet.http.HttpServletRequest)} method and then is written out as a
593      * hidden on the view. Therefore each post done on the view will send back the session id when the view was
594      * rendering, and the {@link org.kuali.rice.krad.web.filter.UifSessionTimeoutFilter} can use that to determine
595      * if a timeout has occurred
596      * </p>
597      *
598      * @return id for the user's current session
599      */
600     public String getSessionId() {
601         return sessionId;
602     }
603 
604     /**
605      * Holds the configured session timeout interval
606      *
607      * <p>
608      * Holds the session timeout interval so it can be referenced to give the user notifications (for example the
609      * session timeout warning reads this property). This is initialized from the session object in
610      * {@link #postBind(javax.servlet.http.HttpServletRequest)}
611      * </p>
612      *
613      * @return amount of time in milliseconds before the session will timeout
614      */
615     public int getSessionTimeoutInterval() {
616         return sessionTimeoutInterval;
617     }
618 
619     /**
620      * Identifies the controller method that should be invoked to fulfill a
621      * request. The value will be matched up against the 'params' setting on the
622      * {@code RequestMapping} annotation for the controller method
623      *
624      * @return String method to call
625      */
626     public String getMethodToCall() {
627         return this.methodToCall;
628     }
629 
630     /**
631      * Setter for the method to call
632      */
633     public void setMethodToCall(String methodToCall) {
634         this.methodToCall = methodToCall;
635     }
636 
637     /**
638      * {@inheritDoc}
639      */
640     @Override
641     public Map<String, String> getViewRequestParameters() {
642         return this.viewRequestParameters;
643     }
644 
645     /**
646      * {@inheritDoc}
647      */
648     @Override
649     public void setViewRequestParameters(Map<String, String> viewRequestParameters) {
650         this.viewRequestParameters = viewRequestParameters;
651     }
652 
653     /**
654      * {@inheritDoc}
655      */
656     @Override
657     public List<String> getReadOnlyFieldsList() {
658         return readOnlyFieldsList;
659     }
660 
661     /**
662      * {@inheritDoc}
663      */
664     @Override
665     public void setReadOnlyFieldsList(List<String> readOnlyFieldsList) {
666         this.readOnlyFieldsList = readOnlyFieldsList;
667     }
668 
669     /**
670      * @see org.kuali.rice.krad.uif.view.ViewModel#getNewCollectionLines()
671      */
672     @Override
673     public Map<String, Object> getNewCollectionLines() {
674         return this.newCollectionLines;
675     }
676 
677     /**
678      * {@inheritDoc}
679      */
680     @Override
681     public void setNewCollectionLines(Map<String, Object> newCollectionLines) {
682         this.newCollectionLines = newCollectionLines;
683     }
684 
685     /**
686      * {@inheritDoc}
687      */
688     @Override
689     public String getTriggerActionId() {
690         return triggerActionId;
691     }
692 
693     /**
694      * {@inheritDoc}
695      */
696     @Override
697     public void setTriggerActionId(String triggerActionId) {
698         this.triggerActionId = triggerActionId;
699     }
700 
701     /**
702      * @see org.kuali.rice.krad.uif.view.ViewModel#getActionParameters()
703      */
704     @Override
705     public Map<String, String> getActionParameters() {
706         return this.actionParameters;
707     }
708 
709     /**
710      * Returns the action parameters map as a {@code Properties} instance
711      *
712      * @return Properties action parameters
713      */
714     public Properties getActionParametersAsProperties() {
715         return KRADUtils.convertMapToProperties(actionParameters);
716     }
717 
718     /**
719      * {@inheritDoc}
720      */
721     @Override
722     public void setActionParameters(Map<String, String> actionParameters) {
723         this.actionParameters = actionParameters;
724     }
725 
726     /**
727      * Retrieves the value for the given action parameter, or empty string if
728      * not found
729      *
730      * @param actionParameterName - name of the action parameter to retrieve value for
731      * @return String parameter value or empty string
732      */
733     public String getActionParamaterValue(String actionParameterName) {
734         if ((actionParameters != null) && actionParameters.containsKey(actionParameterName)) {
735             return actionParameters.get(actionParameterName);
736         }
737 
738         return "";
739     }
740 
741     /**
742      * Returns the action event that was sent in the action parameters (if any)
743      *
744      * <p>
745      * The action event is a special action parameter that can be sent to indicate a type of action being taken. This
746      * can be looked at by the view or components to render differently
747      * </p>
748      *
749      * TODO: make sure action parameters are getting reinitialized on each request
750      *
751      * @return String action event name or blank if action event was not sent
752      */
753     public String getActionEvent() {
754         if ((actionParameters != null) && actionParameters.containsKey(UifConstants.UrlParams.ACTION_EVENT)) {
755             return actionParameters.get(UifConstants.UrlParams.ACTION_EVENT);
756         }
757 
758         return "";
759     }
760 
761     /**
762      * @see org.kuali.rice.krad.uif.view.ViewModel#getClientStateForSyncing()
763      */
764     @Override
765     public Map<String, Object> getClientStateForSyncing() {
766         return clientStateForSyncing;
767     }
768 
769     /**
770      * Setter for the client state
771      */
772     public void setClientStateForSyncing(Map<String, Object> clientStateForSyncing) {
773         this.clientStateForSyncing = clientStateForSyncing;
774     }
775 
776     /**
777      * @see org.kuali.rice.krad.uif.view.ViewModel#getSelectedCollectionLines()
778      */
779     @Override
780     public Map<String, Set<String>> getSelectedCollectionLines() {
781         return selectedCollectionLines;
782     }
783 
784     /**
785      * {@inheritDoc}
786      */
787     @Override
788     public void setSelectedCollectionLines(Map<String, Set<String>> selectedCollectionLines) {
789         this.selectedCollectionLines = selectedCollectionLines;
790     }
791 
792     /**
793      * Holds Set of String identifiers for lines that were selected in a lookup collection results
794      * across multiple pages.
795      * The value in the cache is preserved in the session across multiple requests. This allows for the
796      * server side paging of results to retain the user choices as they move through the pages.
797      *
798      * @return set of identifiers
799      */
800     public Set<String> getSelectedLookupResultsCache() {
801         return selectedLookupResultsCache;
802     }
803 
804     /**
805      * Sets the lookup result selection cache values
806      */
807     public void setSelectedLookupResultsCache(Set<String> selectedLookupResultsCache) {
808         this.selectedLookupResultsCache = selectedLookupResultsCache;
809     }
810 
811     /**
812      * Key string that identifies the form instance in session storage
813      *
814      * <p>
815      * When the view is posted, the previous form instance is retrieved and then
816      * populated from the request parameters. This key string is retrieve the
817      * session form from the session service
818      * </p>
819      *
820      * @return String form session key
821      */
822     public String getFormKey() {
823         return this.formKey;
824     }
825 
826     /**
827      * Setter for the form's session key
828      */
829     public void setFormKey(String formKey) {
830         this.formKey = formKey;
831     }
832 
833     /**
834      * This is the formKey sent on the original request.  It may differ from the actual form key stored in formKey
835      * based on if the form still exists in session by this key or not.
836      *
837      * @return the original requested form key
838      */
839     public String getRequestedFormKey() {
840         return requestedFormKey;
841     }
842 
843     /**
844      * Set the requestedFormKey
845      */
846     public void setRequestedFormKey(String requestedFormKey) {
847         this.requestedFormKey = requestedFormKey;
848     }
849 
850     /**
851      * Indicates whether a redirect has been requested for the view
852      *
853      * @return boolean true if redirect was requested, false if not
854      */
855     public boolean isRequestRedirected() {
856         return requestRedirected;
857     }
858 
859     /**
860      * Setter for the request redirect indicator
861      */
862     public void setRequestRedirected(boolean requestRedirected) {
863         this.requestRedirected = requestRedirected;
864     }
865 
866     /**
867      * Holder for files that are attached through the view
868      *
869      * @return MultipartFile representing the attachment
870      */
871     public MultipartFile getAttachmentFile() {
872         return this.attachmentFile;
873     }
874 
875     /**
876      * Setter for the form's attachment file
877      */
878     public void setAttachmentFile(MultipartFile attachmentFile) {
879         this.attachmentFile = attachmentFile;
880     }
881 
882     /**
883      * @see org.kuali.rice.krad.uif.view.ViewModel#getUpdateComponentId()
884      */
885     @Override
886     public String getUpdateComponentId() {
887         return updateComponentId;
888     }
889 
890     /**
891      * @see org.kuali.rice.krad.uif.view.ViewModel#setUpdateComponentId(java.lang.String)
892      */
893     @Override
894     public void setUpdateComponentId(String updateComponentId) {
895         this.updateComponentId = updateComponentId;
896     }
897 
898     /**
899      * @see org.kuali.rice.krad.uif.view.ViewModel#getUpdateComponent()
900      */
901     public Component getUpdateComponent() {
902         return updateComponent;
903     }
904 
905     /**
906      * @see org.kuali.rice.krad.uif.view.ViewModel#setUpdateComponent(org.kuali.rice.krad.uif.component.Component)
907      */
908     public void setUpdateComponent(Component updateComponent) {
909         this.updateComponent = updateComponent;
910     }
911 
912     /**
913      * @see org.kuali.rice.krad.uif.view.ViewModel#getView()
914      */
915     @Override
916     public View getView() {
917         return this.view;
918     }
919 
920     /**
921      * @see org.kuali.rice.krad.uif.view.ViewModel#setView(org.kuali.rice.krad.uif.view.View)
922      */
923     @Override
924     public void setView(View view) {
925         this.view = view;
926     }
927 
928     /**
929      * Returns an instance of the view's configured view helper service.
930      *
931      * <p>First checks if there is an initialized view containing a view helper instance. If not, and there is
932      * a view id on the form, a call is made to retrieve the view helper instance or class configuration.</p>
933      *
934      * {@inheritDoc}
935      */
936     @Override
937     public ViewHelperService getViewHelperService() {
938         if ((getView() != null) && (getView().getViewHelperService() != null)) {
939             return getView().getViewHelperService();
940         }
941 
942         String viewId = getViewId();
943         if (StringUtils.isBlank(viewId) && (getView() != null)) {
944             viewId = getView().getId();
945         }
946 
947         if (StringUtils.isBlank(viewId)) {
948             return null;
949         }
950 
951         ViewHelperService viewHelperService =
952                 (ViewHelperService) KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBeanProperty(viewId,
953                         UifPropertyPaths.VIEW_HELPER_SERVICE);
954         if (viewHelperService == null) {
955             Class<?> viewHelperServiceClass =
956                     (Class<?>) KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBeanProperty(viewId,
957                             UifPropertyPaths.VIEW_HELPER_SERVICE_CLASS);
958 
959             if (viewHelperServiceClass != null) {
960                 try {
961                     viewHelperService = (ViewHelperService) viewHelperServiceClass.newInstance();
962                 } catch (Exception e) {
963                     throw new RuntimeException("Unable to instantiate view helper class: " + viewHelperServiceClass, e);
964                 }
965             }
966         }
967 
968         return viewHelperService;
969     }
970 
971     /**
972      * {@inheritDoc}
973      */
974     @Override
975     public ViewPostMetadata getViewPostMetadata() {
976         return viewPostMetadata;
977     }
978 
979     /**
980      * @see UifFormBase#getViewPostMetadata()
981      */
982     @Override
983     public void setViewPostMetadata(ViewPostMetadata viewPostMetadata) {
984         this.viewPostMetadata = viewPostMetadata;
985     }
986 
987     /**
988      * Instance of the {@code ViewService} that can be used to retrieve
989      * {@code View} instances
990      *
991      * @return ViewService implementation
992      */
993     protected ViewService getViewService() {
994         return KRADServiceLocatorWeb.getViewService();
995     }
996 
997     /**
998      * The jumpToId for this form, the element with this id will be jumped to automatically
999      * when the form is loaded in the view.
1000      * Using "TOP" or "BOTTOM" will jump to the top or the bottom of the resulting page.
1001      * jumpToId always takes precedence over jumpToName, if set.
1002      *
1003      * @return the jumpToId
1004      */
1005     public String getJumpToId() {
1006         return this.jumpToId;
1007     }
1008 
1009     /**
1010      * @param jumpToId the jumpToId to set
1011      */
1012     public void setJumpToId(String jumpToId) {
1013         this.jumpToId = jumpToId;
1014     }
1015 
1016     /**
1017      * The jumpToName for this form, the element with this name will be jumped to automatically
1018      * when the form is loaded in the view.
1019      * WARNING: jumpToId always takes precedence over jumpToName, if set.
1020      *
1021      * @return the jumpToName
1022      */
1023     public String getJumpToName() {
1024         return this.jumpToName;
1025     }
1026 
1027     /**
1028      * @param jumpToName the jumpToName to set
1029      */
1030     public void setJumpToName(String jumpToName) {
1031         this.jumpToName = jumpToName;
1032     }
1033 
1034     /**
1035      * Field to place focus on when the page loads
1036      * An empty focusId will result in focusing on the first visible input element by default.
1037      *
1038      * @return the focusId
1039      */
1040     public String getFocusId() {
1041         return this.focusId;
1042     }
1043 
1044     /**
1045      * @param focusId the focusId to set
1046      */
1047     public void setFocusId(String focusId) {
1048         this.focusId = focusId;
1049     }
1050 
1051     /**
1052      * True when the form is considered dirty (data has changed from original value), false otherwise
1053      *
1054      * <p>For most scenarios, this flag should NOT be set to true.
1055      * If this is set, it must be managed explicitly by the application.  This flag exists for marking a
1056      * form dirty from a server call, so it must be changed to false when the form is no longer considered dirty.
1057      * The krad save Action and navigate methodToCall resets this flag back to false, but any other setting of
1058      * this flag must be managed by custom configuration/methods, if custom dirtyForm management is needed.</p>
1059      *
1060      * @return true if the form is considered dirty, false otherwise
1061      */
1062     public boolean isDirtyForm() {
1063         return dirtyForm;
1064     }
1065 
1066     /**
1067      * Sets the dirtyForm flag
1068      *
1069      * <p>For most scenarios, this flag should NOT be set to true.
1070      * If this is set, it must be managed explicitly by the application.  This flag exists for marking a
1071      * form dirty from a server call, so it must be changed to false when the form is no longer considered dirty.
1072      * The krad save Action and navigate methodToCall resets this flag back to false, but any other setting of
1073      * this flag must be managed by custom configuration/methods, if custom dirtyForm management is needed.</p>
1074      */
1075     public void setDirtyForm(boolean dirtyForm) {
1076         this.dirtyForm = dirtyForm;
1077     }
1078 
1079     /**
1080      * Set the dirtyForm flag using a String that will be converted to boolean
1081      */
1082     public void setDirtyForm(String dirtyForm) {
1083         if (dirtyForm != null) {
1084             this.dirtyForm = Boolean.parseBoolean(dirtyForm);
1085         }
1086     }
1087 
1088     /**
1089      * Indicates whether the view is rendered within a lightbox
1090      *
1091      * <p>
1092      * Some discussion (for example how a close button behaves) need to change based on whether the
1093      * view is rendered within a lightbox or the standard browser window. This boolean is true when it is
1094      * within a lightbox
1095      * </p>
1096      *
1097      * @return boolean true if view is rendered within a lightbox, false if not
1098      */
1099     public boolean isRenderedInDialog() {
1100         return this.renderedInDialog;
1101     }
1102 
1103     /**
1104      * Setter for the rendered within lightbox indicator
1105      */
1106     public void setRenderedInDialog(boolean renderedInDialog) {
1107         this.renderedInDialog = renderedInDialog;
1108     }
1109 
1110     /**
1111      * Indicates whether the view is rendered within an iframe (this setting must be passed to the View on the url)
1112      *
1113      * @return boolean true if view is rendered within a iframe, false if not
1114      */
1115     public boolean isRenderedInIframe() {
1116         return renderedInIframe;
1117     }
1118 
1119     /**
1120      * @see org.kuali.rice.krad.web.form.UifFormBase#isRenderedInIframe()
1121      */
1122     public void setRenderedInIframe(boolean renderedInIframe) {
1123         this.renderedInIframe = renderedInIframe;
1124     }
1125 
1126     /**
1127      * @see org.kuali.rice.krad.uif.view.ViewModel#isApplyDefaultValues()
1128      */
1129     @Override
1130     public boolean isApplyDefaultValues() {
1131         return applyDefaultValues;
1132     }
1133 
1134     /**
1135      * @see org.kuali.rice.krad.uif.view.ViewModel#setApplyDefaultValues(boolean)
1136      */
1137     @Override
1138     public void setApplyDefaultValues(boolean applyDefaultValues) {
1139         this.applyDefaultValues = applyDefaultValues;
1140     }
1141 
1142     /**
1143      * @see org.kuali.rice.krad.uif.view.ViewModel#isEvaluateFlagsAndModes()
1144      */
1145     public boolean isEvaluateFlagsAndModes() {
1146         return evaluateFlagsAndModes;
1147     }
1148 
1149     /**
1150      * @see org.kuali.rice.krad.uif.view.ViewModel#setEvaluateFlagsAndModes(boolean)
1151      */
1152     public void setEvaluateFlagsAndModes(boolean evaluateFlagsAndModes) {
1153         this.evaluateFlagsAndModes = evaluateFlagsAndModes;
1154     }
1155 
1156     /**
1157      * @see org.kuali.rice.krad.uif.view.ViewModel#isCanEditView()
1158      */
1159     public Boolean isCanEditView() {
1160         return canEditView;
1161     }
1162 
1163     /**
1164      * @see org.kuali.rice.krad.uif.view.ViewModel#setCanEditView(Boolean)
1165      */
1166     public void setCanEditView(Boolean canEditView) {
1167         this.canEditView = canEditView;
1168     }
1169 
1170     /**
1171      * @see org.kuali.rice.krad.uif.view.ViewModel#getActionFlags()
1172      */
1173     public Map<String, Boolean> getActionFlags() {
1174         return actionFlags;
1175     }
1176 
1177     /**
1178      * @see org.kuali.rice.krad.uif.view.ViewModel#setActionFlags(java.util.Map<java.lang.String,java.lang.Boolean>)
1179      */
1180     public void setActionFlags(Map<String, Boolean> actionFlags) {
1181         this.actionFlags = actionFlags;
1182     }
1183 
1184     /**
1185      * @see org.kuali.rice.krad.uif.view.ViewModel#getEditModes()
1186      */
1187     public Map<String, Boolean> getEditModes() {
1188         return editModes;
1189     }
1190 
1191     /**
1192      * @see org.kuali.rice.krad.uif.view.ViewModel#setEditModes(java.util.Map<java.lang.String,java.lang.Boolean>)
1193      */
1194     public void setEditModes(Map<String, Boolean> editModes) {
1195         this.editModes = editModes;
1196     }
1197 
1198     /**
1199      * @see org.kuali.rice.krad.uif.view.ViewModel#getGrowlScript()
1200      */
1201     @Override
1202     public String getGrowlScript() {
1203         return growlScript;
1204     }
1205 
1206     /**
1207      * @see org.kuali.rice.krad.uif.view.ViewModel#setGrowlScript(String)
1208      */
1209     @Override
1210     public void setGrowlScript(String growlScript) {
1211         this.growlScript = growlScript;
1212     }
1213 
1214     /**
1215      * @see org.kuali.rice.krad.uif.view.ViewModel#getState()
1216      */
1217     @Override
1218     public String getState() {
1219         return state;
1220     }
1221 
1222     /**
1223      * @see org.kuali.rice.krad.uif.view.ViewModel#setState(String)
1224      */
1225     @Override
1226     public void setState(String state) {
1227         this.state = state;
1228     }
1229 
1230     /**
1231      * @see org.kuali.rice.krad.uif.view.ViewModel#isAjaxRequest()
1232      */
1233     @Override
1234     public boolean isAjaxRequest() {
1235         return ajaxRequest;
1236     }
1237 
1238     /**
1239      * @see org.kuali.rice.krad.uif.view.ViewModel#setAjaxRequest(boolean)
1240      */
1241     @Override
1242     public void setAjaxRequest(boolean ajaxRequest) {
1243         this.ajaxRequest = ajaxRequest;
1244     }
1245 
1246     /**
1247      * @see org.kuali.rice.krad.uif.view.ViewModel#getAjaxReturnType()
1248      */
1249     @Override
1250     public String getAjaxReturnType() {
1251         return ajaxReturnType;
1252     }
1253 
1254     /**
1255      * @see org.kuali.rice.krad.uif.view.ViewModel#setAjaxReturnType(String)
1256      */
1257     @Override
1258     public void setAjaxReturnType(String ajaxReturnType) {
1259         this.ajaxReturnType = ajaxReturnType;
1260     }
1261 
1262     /**
1263      * @see org.kuali.rice.krad.uif.view.ViewModel#isUpdateComponentRequest()
1264      */
1265     @Override
1266     public boolean isUpdateComponentRequest() {
1267         return isAjaxRequest() && StringUtils.isNotBlank(getAjaxReturnType()) && getAjaxReturnType().equals(
1268                 UifConstants.AjaxReturnTypes.UPDATECOMPONENT.getKey());
1269     }
1270 
1271     /**
1272      * @see org.kuali.rice.krad.uif.view.ViewModel#isUpdateDialogRequest()
1273      */
1274     @Override
1275     public boolean isUpdateDialogRequest() {
1276         return isAjaxRequest() && StringUtils.isNotBlank(getAjaxReturnType()) && getAjaxReturnType().equals(
1277                 UifConstants.AjaxReturnTypes.UPDATEDIALOG.getKey());
1278     }
1279 
1280     /**
1281      * @see org.kuali.rice.krad.uif.view.ViewModel#isUpdatePageRequest()
1282      */
1283     @Override
1284     public boolean isUpdatePageRequest() {
1285         return StringUtils.isNotBlank(getAjaxReturnType()) && getAjaxReturnType().equals(
1286                 UifConstants.AjaxReturnTypes.UPDATEPAGE.getKey());
1287     }
1288 
1289     /**
1290      * @see org.kuali.rice.krad.uif.view.ViewModel#isUpdateNoneRequest()
1291      */
1292     @Override
1293     public boolean isUpdateNoneRequest() {
1294         return StringUtils.isNotBlank(getAjaxReturnType()) && getAjaxReturnType().equals(
1295                 UifConstants.AjaxReturnTypes.UPDATENONE.getKey());
1296     }
1297 
1298     /**
1299      * @see org.kuali.rice.krad.uif.view.ViewModel#isJsonRequest()
1300      */
1301     @Override
1302     public boolean isJsonRequest() {
1303         return StringUtils.isNotBlank(getRequestJsonTemplate());
1304     }
1305 
1306     /**
1307      * @see org.kuali.rice.krad.uif.view.ViewModel#getRequestJsonTemplate()
1308      */
1309     @Override
1310     public String getRequestJsonTemplate() {
1311         return requestJsonTemplate;
1312     }
1313 
1314     /**
1315      * @see org.kuali.rice.krad.uif.view.ViewModel#setRequestJsonTemplate
1316      */
1317     @Override
1318     public void setRequestJsonTemplate(String requestJsonTemplate) {
1319         this.requestJsonTemplate = requestJsonTemplate;
1320     }
1321 
1322     /**
1323      * {@inheritDoc}
1324      */
1325     @Override
1326     public boolean isCollectionPagingRequest() {
1327         return collectionPagingRequest;
1328     }
1329 
1330     /**
1331      * {@inheritDoc}
1332      */
1333     @Override
1334     public void setCollectionPagingRequest(boolean collectionPagingRequest) {
1335         this.collectionPagingRequest = collectionPagingRequest;
1336     }
1337 
1338     /**
1339      * For cases where the request was triggered from within a dialog, we want to show that dialog,
1340      * identified by this id, again.
1341      */
1342     public String getShowDialogId() {
1343         return showDialogId;
1344     }
1345 
1346     /**
1347      * @see UifFormBase#getShowDialogId()
1348      */
1349     public void setShowDialogId(String dialogId) {
1350         this.showDialogId = dialogId;
1351     }
1352 
1353     /**
1354      * Used by the dialog framework to set the dialog id for a return dialog call (when the server has
1355      * triggered a dialog).
1356      *
1357      * <p>Note this is a request only property. On a return call the value for this gets pulled and used to
1358      * create an entry in {@link UifFormBase#getDialogResponses()}</p>
1359      *
1360      * @return String id for the dialog being returned from
1361      */
1362     public String getReturnDialogId() {
1363         return returnDialogId;
1364     }
1365 
1366     /**
1367      * @see UifFormBase#getReturnDialogId()
1368      */
1369     public void setReturnDialogId(String returnDialogId) {
1370         this.returnDialogId = returnDialogId;
1371     }
1372 
1373     /**
1374      * Used by the dialog framework to set the dialog response for a return dialog call (when the server has
1375      * triggered a dialog).
1376      *
1377      * <p>Note this is a request only property. On a return call the value for this gets pulled and used to
1378      * create an entry in {@link UifFormBase#getDialogResponses()}</p>
1379      *
1380      * @return String response for the dialog being returned from
1381      */
1382     public String getReturnDialogResponse() {
1383         return returnDialogResponse;
1384     }
1385 
1386     /**
1387      * @see UifFormBase#getReturnDialogResponse()
1388      */
1389     public void setReturnDialogResponse(String returnDialogResponse) {
1390         this.returnDialogResponse = returnDialogResponse;
1391     }
1392 
1393     /**
1394      * {@inheritDoc}
1395      */
1396     @Override
1397     public Map<String, String> getDialogExplanations() {
1398         return dialogExplanations;
1399     }
1400 
1401     /**
1402      * {@inheritDoc}
1403      */
1404     @Override
1405     public void setDialogExplanations(Map<String, String> dialogExplanations) {
1406         this.dialogExplanations = dialogExplanations;
1407     }
1408 
1409     /**
1410      * {@inheritDoc}
1411      */
1412     @Override
1413     public Map<String, DialogResponse> getDialogResponses() {
1414         return dialogResponses;
1415     }
1416 
1417     /**
1418      * {@inheritDoc}
1419      */
1420     @Override
1421     public DialogResponse getDialogResponse(String dialogId) {
1422         if ((dialogResponses != null) && dialogResponses.containsKey(dialogId)) {
1423             return dialogResponses.get(dialogId);
1424         }
1425 
1426         return null;
1427     }
1428 
1429     /**
1430      * {@inheritDoc}
1431      */
1432     @Override
1433     public void setDialogResponses(Map<String, DialogResponse> dialogResponses) {
1434         this.dialogResponses = dialogResponses;
1435     }
1436 
1437     /**
1438      * @see org.kuali.rice.krad.uif.view.ViewModel#getExtensionData()
1439      */
1440     @Override
1441     public Map<String, Object> getExtensionData() {
1442         return extensionData;
1443     }
1444 
1445     /**
1446      * {@inheritDoc}
1447      */
1448     @Override
1449     public void setExtensionData(Map<String, Object> extensionData) {
1450         this.extensionData = extensionData;
1451     }
1452 
1453     /**
1454      * Http servlet request instance for the current request being processed.
1455      *
1456      * @return HttpServletRequest instance
1457      */
1458     public HttpServletRequest getRequest() {
1459         return request;
1460     }
1461 
1462     /**
1463      * @see UifFormBase#getRequest()
1464      */
1465     public void setRequest(HttpServletRequest request) {
1466         this.request = request;
1467     }
1468 
1469     /**
1470      * The {@code List} that contains all newly added items for the collections on the model
1471      *
1472      * <p>
1473      * This list contains the new items for all the collections on the model.
1474      * </p>
1475      *
1476      * @return List of the newly added item lists
1477      */
1478     public List getAddedCollectionItems() {
1479         return addedCollectionItems;
1480     }
1481 
1482     /**
1483      * Setter for the newly added item list
1484      */
1485     public void setAddedCollectionItems(List addedCollectionItems) {
1486         this.addedCollectionItems = addedCollectionItems;
1487     }
1488 
1489     /**
1490      * Indicates whether an collection item has been newly added
1491      *
1492      * <p>
1493      * Tests collection items against the list of newly added items on the model. This list gets cleared when the view
1494      * is submitted and the items are persisted.
1495      * </p>
1496      *
1497      * @param item - the item to test against list of newly added items
1498      * @return boolean true if the item has been newly added
1499      */
1500     public boolean isAddedCollectionItem(Object item) {
1501         return addedCollectionItems.contains(item);
1502     }
1503 
1504     /**
1505      * The data object to bind to for a dialog
1506      *
1507      * <p>The data object serves as a placeholder for temporary properties that might be used within a dialog. The
1508      * purpose of placeholder is to provide a separation between the dialog object and the underlying object for use
1509      * in cases like object manipulation.</p>
1510      */
1511     public Object getDialogDataObject() {
1512         return dialogDataObject;
1513     }
1514 
1515     /**
1516      * @see UifFormBase#getDialogDataObject()
1517      */
1518     public void setDialogDataObject(Object dataObject) {
1519         this.dialogDataObject = dataObject;
1520     }
1521 
1522     @Override
1523     public String toString() {
1524         StringBuilder builder = new StringBuilder();
1525         builder.append(getClass().getSimpleName()).append(" [viewId=").append(this.viewId).append(", viewName=").append(
1526                 this.viewName).append(", viewTypeName=").append(this.viewTypeName).append(", pageId=").append(
1527                 this.pageId).append(", methodToCall=").append(this.methodToCall).append(", formKey=").append(
1528                 this.formKey).append(", requestedFormKey=").append(this.requestedFormKey).append("]");
1529         return builder.toString();
1530     }
1531 
1532     public String getCsrfToken() {
1533         return csrfToken;
1534     }
1535 
1536     public void setCsrfToken(String csrfToken) {
1537         this.csrfToken = csrfToken;
1538     }
1539 
1540 }