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