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