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