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