View Javadoc

1   /*
2    * Copyright 2007 The Kuali Foundation Licensed under the Educational Community
3    * License, Version 1.0 (the "License"); you may not use this file except in
4    * compliance with the License. You may obtain a copy of the License at
5    * http://www.opensource.org/licenses/ecl1.php Unless required by applicable law
6    * or agreed to in writing, software distributed under the License is
7    * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
8    * KIND, either express or implied. See the License for the specific language
9    * governing permissions and limitations under the License.
10   */
11  package org.kuali.rice.krad.uif.view;
12  
13  import org.apache.commons.lang.StringUtils;
14  import org.kuali.rice.krad.uif.UifConstants;
15  import org.kuali.rice.krad.uif.UifConstants.ViewStatus;
16  import org.kuali.rice.krad.uif.UifConstants.ViewType;
17  import org.kuali.rice.krad.uif.authorization.Authorizer;
18  import org.kuali.rice.krad.uif.authorization.PresentationController;
19  import org.kuali.rice.krad.uif.container.ContainerBase;
20  import org.kuali.rice.krad.uif.container.Group;
21  import org.kuali.rice.krad.uif.container.NavigationGroup;
22  import org.kuali.rice.krad.uif.container.PageGroup;
23  import org.kuali.rice.krad.uif.component.BindingInfo;
24  import org.kuali.rice.krad.uif.component.Component;
25  import org.kuali.rice.krad.uif.component.ReferenceCopy;
26  import org.kuali.rice.krad.uif.component.RequestParameter;
27  import org.kuali.rice.krad.uif.field.HeaderField;
28  import org.kuali.rice.krad.uif.field.LinkField;
29  import org.kuali.rice.krad.uif.service.ViewHelperService;
30  import org.kuali.rice.krad.uif.util.BooleanMap;
31  import org.kuali.rice.krad.uif.util.ClientValidationUtils;
32  import org.kuali.rice.krad.uif.widget.BreadCrumbs;
33  import org.kuali.rice.krad.uif.widget.GrowlsWidget;
34  import org.kuali.rice.krad.util.ObjectUtils;
35  
36  import java.util.ArrayList;
37  import java.util.HashMap;
38  import java.util.HashSet;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Set;
42  
43  /**
44   * Root of the component tree which encompasses a set of related
45   * <code>GroupContainer</code> instances tied together with a common page layout
46   * and navigation.
47   *
48   * <p>
49   * The <code>View</code> component ties together all the components and
50   * configuration of the User Interface for a piece of functionality. In Rice
51   * applications the view is typically associated with a <code>Document</code>
52   * instance.
53   * </p>
54   *
55   * <p>
56   * The view template lays out the common header, footer, and navigation for the
57   * related pages. In addition the view renders the HTML head element bringing in
58   * common script files and style sheets, along with optionally rendering a form
59   * element for pages that need to post data back to the server.
60   * </p>
61   *
62   * <p>
63   * Configuration of UIF features such as model validation is also done through
64   * the <code>View</code>
65   * </p>
66   *
67   * @author Kuali Rice Team (rice.collab@kuali.org)
68   */
69  public class View extends ContainerBase {
70      private static final long serialVersionUID = -1220009725554576953L;
71  
72      private String viewName;
73  
74      private int idSequence;
75  
76      // application
77      private HeaderField applicationHeader;
78      private Group applicationFooter;
79  
80      // Breadcrumbs
81      private BreadCrumbs breadcrumbs;
82      private String viewLabelFieldPropertyName;
83      private String appendOption;
84      private boolean breadcrumbsInApplicationHeader;
85  
86      // Growls support
87      private GrowlsWidget growlsWidget;
88      private boolean growlMessagingEnabled;
89  
90      private String entryPageId;
91  
92      @RequestParameter
93      private String currentPageId;
94  
95      private NavigationGroup navigation;
96  
97      private Class<?> formClass;
98      private String defaultBindingObjectPath;
99      private Map<String, Class<?>> abstractTypeClasses;
100 
101     private List<String> additionalScriptFiles;
102     private List<String> additionalCssFiles;
103 
104     private ViewType viewTypeName;
105     private Class<? extends ViewHelperService> viewHelperServiceClassName;
106 
107     private String viewStatus;
108     private ViewIndex viewIndex;
109     private Map<String, String> viewRequestParameters;
110 
111     private Class<? extends PresentationController> presentationControllerClass;
112     private Class<? extends Authorizer> authorizerClass;
113 
114     private BooleanMap actionFlags;
115     private BooleanMap editModes;
116 
117     private Map<String, String> expressionVariables;
118 
119     private boolean singlePageView;
120     private PageGroup page;
121 
122     private List<? extends Group> items;
123 
124     private LinkField viewMenuLink;
125     private String viewMenuGrouping;
126 
127     private boolean validateDirty;
128     private boolean translateCodes;
129     private String preLoadScript;
130     private Map<String, Object> clientSideState;
131 
132     @RequestParameter
133     private boolean dialogMode;
134 
135     @ReferenceCopy
136     private ViewHelperService viewHelperService;
137 
138     public View() {
139         dialogMode = false;
140         singlePageView = false;
141         translateCodes = false;
142         viewTypeName = ViewType.DEFAULT;
143         viewStatus = UifConstants.ViewStatus.CREATED;
144         breadcrumbsInApplicationHeader = false;
145 
146         idSequence = 0;
147         this.viewIndex = new ViewIndex();
148 
149         additionalScriptFiles = new ArrayList<String>();
150         additionalCssFiles = new ArrayList<String>();
151         items = new ArrayList<Group>();
152         abstractTypeClasses = new HashMap<String, Class<?>>();
153         viewRequestParameters = new HashMap<String, String>();
154         expressionVariables = new HashMap<String, String>();
155         clientSideState = new HashMap<String, Object>();
156     }
157 
158     /**
159      * The following initialization is performed:
160      *
161      * <ul>
162      * <li>If current page id is not set, it is set to the configured entry page
163      * or first item in list id</li>
164      * <li>If a single paged view, set items in page group and put the page in
165      * the items list</li>
166      * </ul>
167      *
168      * @see org.kuali.rice.krad.uif.container.ContainerBase#performInitialization(View, java.lang.Object)
169      */
170     @SuppressWarnings("unchecked")
171     @Override
172     public void performInitialization(View view, Object model) {
173         super.performInitialization(view, model);
174 
175         // populate items on page for single paged view
176         if (singlePageView) {
177             if (page != null) {
178                 page.setItems(new ArrayList<Group>(items));
179 
180                 // reset the items list to include the one page
181                 items = new ArrayList<Group>();
182                 ((List<Group>) items).add(page);
183             } else {
184                 throw new RuntimeException("For single paged views the page Group must be set.");
185             }
186         }
187 
188         // make sure all the pages have ids before selecting the current page
189         for (Group group : this.getItems()) {
190             if (StringUtils.isBlank(group.getId())) {
191                 group.setId(view.getNextId());
192             }
193         }
194 
195         // default current page if not set
196         if (StringUtils.isBlank(currentPageId)) {
197             if (StringUtils.isNotBlank(entryPageId)) {
198                 currentPageId = entryPageId;
199             } else {
200                 Group firstPageGroup = getItems().get(0);
201                 currentPageId = firstPageGroup.getId();
202             }
203         }
204     }
205 
206     /**
207      * The following is performed:
208      *
209      * <ul>
210      * <li>Adds to its document ready script the setupValidator js function for setting
211      * up the validator for this view</li>
212      * </ul>
213      *
214      * @see org.kuali.rice.krad.uif.container.ContainerBase#performFinalize(View,
215      *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
216      */
217     @Override
218     public void performFinalize(View view, Object model, Component parent) {
219         super.performFinalize(view, model, parent);
220 
221         String prefixScript = "";
222         if (this.getOnDocumentReadyScript() != null) {
223             prefixScript = this.getOnDocumentReadyScript();
224         }
225         this.setOnDocumentReadyScript(prefixScript + "jQuery.extend(jQuery.validator.messages, " +
226                 ClientValidationUtils.generateValidatorMessagesOption() + ");");
227     }
228 
229     /**
230      * @see org.kuali.rice.krad.uif.component.ComponentBase#getNestedComponents()
231      */
232     @Override
233     public List<Component> getNestedComponents() {
234         List<Component> components = super.getNestedComponents();
235 
236         components.add(applicationHeader);
237         components.add(applicationFooter);
238         components.add(navigation);
239         components.add(breadcrumbs);
240         components.add(growlsWidget);
241         components.add(viewMenuLink);
242 
243         // remove all pages that are not the current page
244         if (!singlePageView) {
245             for (Group group : this.getItems()) {
246                 if ((group instanceof PageGroup) && !StringUtils.equals(group.getId(), getCurrentPageId()) && components.contains(
247                         group)) {
248                     components.remove(group);
249                 }
250             }
251         }
252 
253         return components;
254     }
255 
256     /**
257      * @see org.kuali.rice.krad.web.view.container.ContainerBase#getSupportedComponents()
258      */
259     @Override
260     public Set<Class<? extends Component>> getSupportedComponents() {
261         Set<Class<? extends Component>> supportedComponents = new HashSet<Class<? extends Component>>();
262         supportedComponents.add(Group.class);
263 
264         return supportedComponents;
265     }
266 
267     /**
268      * @see org.kuali.rice.krad.uif.component.Component#getComponentTypeName()
269      */
270     @Override
271     public String getComponentTypeName() {
272         return "view";
273     }
274 
275     /**
276      * Iterates through the contained page items and returns the Page that
277      * matches the set current page id
278      *
279      * @return Page instance
280      */
281     public PageGroup getCurrentPage() {
282         for (Group pageGroup : this.getItems()) {
283             if (StringUtils.equals(pageGroup.getId(), getCurrentPageId()) && pageGroup instanceof PageGroup) {
284                 return (PageGroup) pageGroup;
285             }
286         }
287 
288         return null;
289     }
290 
291     /**
292      * View name provides an identifier for a view within a type. That is if a
293      * set of <code>View</code> instances have the same values for the
294      * properties that are used to retrieve them by their type, the name can be
295      * given to further qualify the view that should be retrieved.
296      * <p>
297      * A view type like the <code>LookupView</code> might have several views for
298      * the same object class, but one that is the 'default' lookup and another
299      * that is the 'advanced' lookup. Therefore the name on the first could be
300      * set to 'default', and likewise the name for the second 'advanced'.
301      * </p>
302      *
303      * @return String name of view
304      */
305     public String getViewName() {
306         return this.viewName;
307     }
308 
309     /**
310      * Setter for the view's name
311      *
312      * @param viewName
313      */
314     public void setViewName(String viewName) {
315         this.viewName = viewName;
316     }
317 
318     /**
319      * Header for the application containing the view
320      *
321      * <p>
322      * When deploying outside a portal, the application header and footer property can be configured to
323      * display a consistent header/footer across all views. Here application logos, menus, login controls
324      * and so on can be rendered.
325      * </p>
326      *
327      * @return HeaderField application header
328      */
329     public HeaderField getApplicationHeader() {
330         return applicationHeader;
331     }
332 
333     /**
334      * Setter for the application header
335      *
336      * @param applicationHeader
337      */
338     public void setApplicationHeader(HeaderField applicationHeader) {
339         this.applicationHeader = applicationHeader;
340     }
341 
342     /**
343      * Footer for the application containing the view
344      *
345      * <p>
346      * When deploying outside a portal, the application header and footer property can be configured to
347      * display a consistent header/footer across all views. Here such things as application links, copyrights
348      * and so on can be rendered.
349      * </p>
350      *
351      * @return Group application footer
352      */
353     public Group getApplicationFooter() {
354         return applicationFooter;
355     }
356 
357     /**
358      * Setter for the application footer
359      *
360      * @param applicationFooter
361      */
362     public void setApplicationFooter(Group applicationFooter) {
363         this.applicationFooter = applicationFooter;
364     }
365 
366     /**
367      * Returns the next unique id available for components within the view instance
368      *
369      * @return String next id available
370      */
371     public String getNextId() {
372         return Integer.toString(idSequence++);
373     }
374 
375     /**
376      * Specifies what page should be rendered by default. This is the page that
377      * will be rendered when the <code>View</code> is first rendered or when the
378      * current page is not set
379      *
380      * @return String id of the page to render by default
381      */
382     public String getEntryPageId() {
383         return this.entryPageId;
384     }
385 
386     /**
387      * Setter for default Page id
388      *
389      * @param entryPageId
390      */
391     public void setEntryPageId(String entryPageId) {
392         this.entryPageId = entryPageId;
393     }
394 
395     /**
396      * The id for the page within the view that should be displayed in the UI.
397      * Other pages of the view will not be rendered
398      *
399      * @return String id of the page that should be displayed
400      */
401     public String getCurrentPageId() {
402         return this.currentPageId;
403     }
404 
405     /**
406      * Setter for the page id to display
407      *
408      * @param currentPageId
409      */
410     public void setCurrentPageId(String currentPageId) {
411         this.currentPageId = currentPageId;
412     }
413 
414     /**
415      * <code>NavigationGroup</code> instance for the <code>View</code>
416      * <p>
417      * Provides configuration necessary to render the navigation. This includes
418      * navigation items in addition to configuration for the navigation
419      * renderer.
420      * </p>
421      *
422      * @return NavigationGroup
423      */
424     public NavigationGroup getNavigation() {
425         return this.navigation;
426     }
427 
428     /**
429      * Setter for the View's <code>NavigationGroup</code>
430      *
431      * @param navigation
432      */
433     public void setNavigation(NavigationGroup navigation) {
434         this.navigation = navigation;
435     }
436 
437     /**
438      * Class of the Form that should be used with the <code>View</code>
439      * instance. The form is the top level object for all the view's data and is
440      * used to present and accept data in the user interface. All form classes
441      * should extend UifFormBase
442      *
443      * @return Class<?> class for the view's form
444      * @see org.kuali.rice.krad.web.form.UifFormBase
445      */
446     public Class<?> getFormClass() {
447         return this.formClass;
448     }
449 
450     /**
451      * Setter for the form class
452      *
453      * @param formClass
454      */
455     public void setFormClass(Class<?> formClass) {
456         this.formClass = formClass;
457     }
458 
459     /**
460      * For <code>View</code> types that work primarily with one nested object of
461      * the form (for instance document, or bo) the default binding object path
462      * can be set for each of the views <code>DataBinding</code> components. If
463      * the component does not set its own binding object path it will inherit
464      * the default
465      *
466      * @return String binding path to the object from the form
467      * @see org.kuali.rice.krad.uif.BindingInfo.getBindingObjectPath()
468      */
469     public String getDefaultBindingObjectPath() {
470         return this.defaultBindingObjectPath;
471     }
472 
473     /**
474      * Setter for the default binding object path to use for the view
475      *
476      * @param defaultBindingObjectPath
477      */
478     public void setDefaultBindingObjectPath(String defaultBindingObjectPath) {
479         this.defaultBindingObjectPath = defaultBindingObjectPath;
480     }
481 
482     /**
483      * Configures the concrete classes that will be used for properties in the
484      * form object graph that have an abstract or interface type
485      *
486      * <p>
487      * For properties that have an abstract or interface type, it is not
488      * possible to perform operations like getting/settings property values and
489      * getting information from the dictionary. When these properties are
490      * encountered in the object graph, this <code>Map</code> will be consulted
491      * to determine the concrete type to use.
492      * </p>
493      *
494      * <p>
495      * e.g. Suppose we have a property document.accountingLine.accountNumber and
496      * the accountingLine property on the document instance has an interface
497      * type 'AccountingLine'. We can then put an entry into this map with key
498      * 'document.accountingLine', and value
499      * 'org.kuali.rice.sampleapp.TravelAccountingLine'. When getting the
500      * property type or an entry from the dictionary for accountNumber, the
501      * TravelAccountingLine class will be used.
502      * </p>
503      *
504      * @return Map<String, Class> of class implementations keyed by path
505      */
506     public Map<String, Class<?>> getAbstractTypeClasses() {
507         return this.abstractTypeClasses;
508     }
509 
510     /**
511      * Setter for the Map of class implementations keyed by path
512      *
513      * @param abstractTypeClasses
514      */
515     public void setAbstractTypeClasses(Map<String, Class<?>> abstractTypeClasses) {
516         this.abstractTypeClasses = abstractTypeClasses;
517     }
518 
519     /**
520      * Declares additional script files that should be included with the
521      * <code>View</code>. These files are brought into the HTML page along with
522      * common script files configured for the Rice application. Each entry
523      * contain the path to the CSS file, either a relative path, path from web
524      * root, or full URI
525      * <p>
526      * e.g. '/krad/scripts/myScript.js', '../scripts/myScript.js',
527      * 'http://my.edu/web/myScript.js'
528      * </p>
529      *
530      * @return List<String> script file locations
531      */
532     public List<String> getAdditionalScriptFiles() {
533         return this.additionalScriptFiles;
534     }
535 
536     /**
537      * Setter for the List of additional script files to included with the
538      * <code>View</code>
539      *
540      * @param additionalScriptFiles
541      */
542     public void setAdditionalScriptFiles(List<String> additionalScriptFiles) {
543         this.additionalScriptFiles = additionalScriptFiles;
544     }
545 
546     /**
547      * Declares additional CSS files that should be included with the
548      * <code>View</code>. These files are brought into the HTML page along with
549      * common CSS files configured for the Rice application. Each entry should
550      * contain the path to the CSS file, either a relative path, path from web
551      * root, or full URI
552      * <p>
553      * e.g. '/krad/css/stacked-view.css', '../css/stacked-view.css',
554      * 'http://my.edu/web/stacked-view.css'
555      * </p>
556      *
557      * @return List<String> CSS file locations
558      */
559     public List<String> getAdditionalCssFiles() {
560         return this.additionalCssFiles;
561     }
562 
563     /**
564      * Setter for the List of additional CSS files to included with the
565      * <code>View</code>
566      *
567      * @param additionalCssFiles
568      */
569     public void setAdditionalCssFiles(List<String> additionalCssFiles) {
570         this.additionalCssFiles = additionalCssFiles;
571     }
572 
573     public boolean isDialogMode() {
574         return this.dialogMode;
575     }
576 
577     public void setDialogMode(boolean dialogMode) {
578         this.dialogMode = dialogMode;
579     }
580 
581     /**
582      * View type name the view is associated with the view instance
583      *
584      * <p>
585      * Views that share common features and functionality can be grouped by the
586      * view type. Usually view types extend the <code>View</code> class to
587      * provide additional configuration and to set defaults. View types can also
588      * implement the <code>ViewTypeService</code> to add special indexing and
589      * retrieval of views.
590      * </p>
591      *
592      * @return String view type name for the view
593      */
594     public ViewType getViewTypeName() {
595         return this.viewTypeName;
596     }
597 
598     /**
599      * Setter for the view's type name
600      *
601      * @param viewTypeName
602      */
603     public void setViewTypeName(ViewType viewTypeName) {
604         this.viewTypeName = viewTypeName;
605     }
606 
607     /**
608      * Class name of the <code>ViewHelperService</code> that handles the various
609      * phases of the Views lifecycle
610      *
611      * @return Class for the spring bean
612      * @see org.kuali.rice.krad.uif.service.ViewHelperService
613      */
614     public Class<? extends ViewHelperService> getViewHelperServiceClassName() {
615         return this.viewHelperServiceClassName;
616     }
617 
618     /**
619      * Setter for the <code>ViewHelperService</code> class name
620      *
621      * @param viewLifecycleService
622      */
623     public void setViewHelperServiceClassName(Class<? extends ViewHelperService> viewHelperServiceClassName) {
624         this.viewHelperServiceClassName = viewHelperServiceClassName;
625     }
626 
627     /**
628      * Creates the <code>ViewHelperService</code> associated with the View
629      *
630      * @return ViewHelperService instance
631      */
632     public ViewHelperService getViewHelperService() {
633         if (this.viewHelperService == null) {
634             viewHelperService = ObjectUtils.newInstance(viewHelperServiceClassName);
635         }
636 
637         return viewHelperService;
638     }
639 
640     /**
641      * Invoked to produce a ViewIndex of the current view's components
642      */
643     public void index() {
644         if (this.viewIndex == null) {
645             this.viewIndex = new ViewIndex();
646         }
647         this.viewIndex.index(this);
648     }
649 
650     /**
651      * Holds field indexes of the <code>View</code> instance for retrieval
652      *
653      * @return ViewIndex instance
654      */
655     public ViewIndex getViewIndex() {
656         return this.viewIndex;
657     }
658 
659     /**
660      * Map of parameters from the request that set view options, used to rebuild
661      * the view on each post
662      * <p>
663      * Views can be configured by parameters. These might impact which parts of
664      * the view are rendered or how the view behaves. Generally these would get
665      * passed in when a new view is requested (by request parameters). These
666      * will be used to initially populate the view properties. In addition, on a
667      * post the view will be rebuilt and properties reset again by the allow
668      * request parameters.
669      * </p>
670      * <p>
671      * Example parameter would be for MaintenaceView whether a New, Edit, or
672      * Copy was requested (maintenance mode)
673      * </p>
674      *
675      * @return
676      */
677     public Map<String, String> getViewRequestParameters() {
678         return this.viewRequestParameters;
679     }
680 
681     /**
682      * Setter for the view's request parameters map
683      *
684      * @param viewRequestParameters
685      */
686     public void setViewRequestParameters(Map<String, String> viewRequestParameters) {
687         this.viewRequestParameters = viewRequestParameters;
688     }
689 
690     /**
691      * PresentationController class that should be used for the
692      * <code>View</code> instance
693      * <p>
694      * The presentation controller is consulted to determine component (group,
695      * field) state such as required, read-only, and hidden. The presentation
696      * controller does not take into account user permissions. The presentation
697      * controller can also output action flags and edit modes that will be set
698      * onto the view instance and can be referred to by conditional expressions
699      * </p>
700      *
701      * @return Class<? extends PresentationController>
702      * @see View.getActionFlags()
703      * @see View.getEditModes()
704      */
705     public Class<? extends PresentationController> getPresentationControllerClass() {
706         return this.presentationControllerClass;
707     }
708 
709     /**
710      * Setter for the view's presentation controller
711      *
712      * @param presentationControllerClass
713      */
714     public void setPresentationControllerClass(Class<? extends PresentationController> presentationControllerClass) {
715         this.presentationControllerClass = presentationControllerClass;
716     }
717 
718     /**
719      * Authorizer class that should be used for the <code>View</code> instance
720      * <p>
721      * The authorizer class is consulted to determine component (group, field)
722      * state such as required, read-only, and hidden based on the users
723      * permissions. It typically communicates with the Kuali Identity Management
724      * system to determine roles and permissions. It is used with the
725      * presentation controller and dictionary conditional logic to determine the
726      * final component state. The authorizer can also output action flags and
727      * edit modes that will be set onto the view instance and can be referred to
728      * by conditional expressions
729      * </p>
730      *
731      * @return Class<? extends Authorizer>
732      * @see View.getActionFlags()
733      * @see View.getEditModes()
734      */
735     public Class<? extends Authorizer> getAuthorizerClass() {
736         return this.authorizerClass;
737     }
738 
739     /**
740      * Setter for the view's authorizer
741      *
742      * @param authorizerClass
743      */
744     public void setAuthorizerClass(Class<? extends Authorizer> authorizerClass) {
745         this.authorizerClass = authorizerClass;
746     }
747 
748     /**
749      * Map of strings that flag what actions can be taken in the UI
750      * <p>
751      * These can be used in conditional expressions in the dictionary or by
752      * other UI logic
753      * </p>
754      *
755      * @return BooleanMap action flags
756      */
757     public BooleanMap getActionFlags() {
758         return this.actionFlags;
759     }
760 
761     /**
762      * Setter for the action flags Map
763      *
764      * @param actionFlags
765      */
766     public void setActionFlags(BooleanMap actionFlags) {
767         this.actionFlags = actionFlags;
768     }
769 
770     /**
771      * Map of edit modes that enabled for the view
772      * <p>
773      * These can be used in conditional expressions in the dictionary or by
774      * other UI logic
775      * </p>
776      *
777      * @return BooleanMap edit modes
778      */
779     public BooleanMap getEditModes() {
780         return this.editModes;
781     }
782 
783     /**
784      * Setter for the edit modes Map
785      *
786      * @param editModes
787      */
788     public void setEditModes(BooleanMap editModes) {
789         this.editModes = editModes;
790     }
791 
792     /**
793      * Map that contains expressions to evaluate and make available as variables
794      * for conditional expressions within the view
795      * <p>
796      * Each Map entry contains one expression variables, where the map key gives
797      * the name for the variable, and the map value gives the variable
798      * expression. The variables expressions will be evaluated before
799      * conditional logic is run and made available as variables for other
800      * conditional expressions. Variable expressions can be based on the model
801      * and any object contained in the view's context
802      * </p>
803      *
804      * @return Map<String, String> variable expressions
805      */
806     public Map<String, String> getExpressionVariables() {
807         return this.expressionVariables;
808     }
809 
810     /**
811      * Setter for the view's map of variable expressions
812      *
813      * @param expressionVariables
814      */
815     public void setExpressionVariables(Map<String, String> expressionVariables) {
816         this.expressionVariables = expressionVariables;
817     }
818 
819     /**
820      * Indicates whether the <code>View</code> only has a single page
821      * <code>Group</code> or contains multiple page <code>Group</code>
822      * instances. In the case of a single page it is assumed the group's items
823      * list contains the section groups for the page, and the page itself is
824      * given by the page property ({@link #getPage()}. This is for convenience
825      * of configuration and also can drive other configuration like styling.
826      *
827      * @return boolean true if the view only contains one page group, false if
828      *         it contains multple pages
829      */
830     public boolean isSinglePageView() {
831         return this.singlePageView;
832     }
833 
834     /**
835      * Setter for the single page indicator
836      *
837      * @param singlePageView
838      */
839     public void setSinglePageView(boolean singlePageView) {
840         this.singlePageView = singlePageView;
841     }
842 
843     /**
844      * For single paged views ({@link #isSinglePageView()}, gives the page
845      * <code>Group</code> the view should render. The actual items for the page
846      * is taken from the group's items list ({@link #getItems()}, and set onto
847      * the give page group. This is for convenience of configuration.
848      *
849      * @return Group page group for single page views
850      */
851     public PageGroup getPage() {
852         return this.page;
853     }
854 
855     /**
856      * Setter for the page group for single page views
857      *
858      * @param page
859      */
860     public void setPage(PageGroup page) {
861         this.page = page;
862     }
863 
864     /**
865      * @see org.kuali.rice.krad.uif.container.ContainerBase#getItems()
866      */
867     @Override
868     public List<? extends Group> getItems() {
869         return this.items;
870     }
871 
872     /**
873      * Setter for the view's <code>Group</code> instances
874      *
875      * @param items
876      */
877     @Override
878     public void setItems(List<? extends Component> items) {
879         // TODO: fix this generic issue
880         this.items = (List<? extends Group>) items;
881     }
882 
883     /**
884      * Provides configuration for displaying a link to the view from an
885      * application menu
886      *
887      * @return LinkField view link field
888      */
889     public LinkField getViewMenuLink() {
890         return this.viewMenuLink;
891     }
892 
893     /**
894      * Setter for the views link field
895      *
896      * @param viewMenuLink
897      */
898     public void setViewMenuLink(LinkField viewMenuLink) {
899         this.viewMenuLink = viewMenuLink;
900     }
901 
902     /**
903      * Provides a grouping string for the view to group its menu link (within a
904      * portal for instance)
905      *
906      * @return String menu grouping
907      */
908     public String getViewMenuGrouping() {
909         return this.viewMenuGrouping;
910     }
911 
912     /**
913      * Setter for the views menu grouping
914      *
915      * @param viewMenuGrouping
916      */
917     public void setViewMenuGrouping(String viewMenuGrouping) {
918         this.viewMenuGrouping = viewMenuGrouping;
919     }
920 
921     /**
922      * Indicates what lifecycle phase the View instance is in
923      * <p>
924      * The view lifecycle begins with the CREATED status. In this status a new
925      * instance of the view has been retrieved from the dictionary, but no
926      * further processing has been done. After the initialize phase has been run
927      * the status changes to INITIALIZED. After the model has been applied and
928      * the view is ready for render the status changes to FINAL
929      * </p>
930      *
931      * @return String view status
932      * @see org.kuali.rice.krad.uif.UifConstants.ViewStatus
933      */
934     public String getViewStatus() {
935         return this.viewStatus;
936     }
937 
938     /**
939      * Setter for the view status
940      *
941      * @param viewStatus
942      */
943     public void setViewStatus(String viewStatus) {
944         this.viewStatus = viewStatus;
945     }
946 
947     /**
948      * Indicates whether the view has been initialized
949      *
950      * @return boolean true if the view has been initialized, false if not
951      */
952     public boolean isInitialized() {
953         return StringUtils.equals(viewStatus, ViewStatus.INITIALIZED) ||
954                 StringUtils.equals(viewStatus, ViewStatus.FINAL);
955     }
956 
957     /**
958      * Indicates whether the view has been updated from the model and final
959      * updates made
960      *
961      * @return boolean true if the view has been updated, false if not
962      */
963     public boolean isFinal() {
964         return StringUtils.equals(viewStatus, ViewStatus.FINAL);
965     }
966 
967     /**
968      * onSubmit script configured on the <code>View</code> gets placed on the
969      * form element
970      *
971      * @see org.kuali.rice.krad.uif.component.ComponentBase#getSupportsOnSubmit()
972      */
973     @Override
974     public boolean getSupportsOnSubmit() {
975         return true;
976     }
977 
978     /**
979      * onLoad script configured on the <code>View</code> gets placed in a load
980      * call
981      *
982      * @see org.kuali.rice.krad.uif.component.ComponentBase#getSupportsOnLoad()
983      */
984     @Override
985     public boolean getSupportsOnLoad() {
986         return true;
987     }
988 
989     /**
990      * onDocumentReady script configured on the <code>View</code> gets placed in
991      * a document ready jQuery block
992      *
993      * @see org.kuali.rice.krad.uif.component.ComponentBase#getSupportsOnLoad()
994      */
995     @Override
996     public boolean getSupportsOnDocumentReady() {
997         return true;
998     }
999 
1000     /**
1001      * Breadcrumb widget used for displaying homeward path and history
1002      *
1003      * @return the breadcrumbs
1004      */
1005     public BreadCrumbs getBreadcrumbs() {
1006         return this.breadcrumbs;
1007     }
1008 
1009     /**
1010      * @param breadcrumbs the breadcrumbs to set
1011      */
1012     public void setBreadcrumbs(BreadCrumbs breadcrumbs) {
1013         this.breadcrumbs = breadcrumbs;
1014     }
1015 
1016     /**
1017      * Indicates whether the breadcrumbs are rendered in the application header and should not
1018      * be rendered as part of the view template
1019      *
1020      * <p>
1021      * For layout purposes it is sometimes necessary to render the breadcrumbs in the application header. This flag
1022      * indicates that is being done and therefore should not be rendered in the view template.
1023      * </p>
1024      *
1025      * @return boolean true if breadcrumbs are rendered in the application header, false if not and they should be
1026      *         rendered with the view
1027      */
1028     public boolean isBreadcrumbsInApplicationHeader() {
1029         return breadcrumbsInApplicationHeader;
1030     }
1031 
1032     /**
1033      * Setter for the breadcrumbs in application header indicator
1034      *
1035      * @param breadcrumbsInApplicationHeader
1036      */
1037     public void setBreadcrumbsInApplicationHeader(boolean breadcrumbsInApplicationHeader) {
1038         this.breadcrumbsInApplicationHeader = breadcrumbsInApplicationHeader;
1039     }
1040 
1041     /**
1042      * Growls widget which sets up global settings for the growls used in this
1043      * view and its pages
1044      *
1045      * @return the growlsWidget
1046      */
1047     public GrowlsWidget getGrowlsWidget() {
1048         return this.growlsWidget;
1049     }
1050 
1051     /**
1052      * @param growlsWidget the growlsWidget to set
1053      */
1054     public void setGrowlsWidget(GrowlsWidget growlsWidget) {
1055         this.growlsWidget = growlsWidget;
1056     }
1057 
1058     /**
1059      * Growls use the messages contained in the message map. If enabled, info
1060      * messages in their entirety will be displayed in growls, for warning and
1061      * error messages a growl message will notify the user that these messages
1062      * exist on the page. If this setting is disabled, it is recommended that
1063      * infoMessage display be enabled for the page ErrorsField bean in order to
1064      * display relevant information to the user. Note: the growl scripts are
1065      * built out in the PageGroup class.
1066      *
1067      * @return the growlMessagingEnabled
1068      */
1069     public boolean isGrowlMessagingEnabled() {
1070         return this.growlMessagingEnabled;
1071     }
1072 
1073     /**
1074      * @param growlMessagingEnabled the growlMessagingEnabled to set
1075      */
1076     public void setGrowlMessagingEnabled(boolean growlMessagingEnabled) {
1077         this.growlMessagingEnabled = growlMessagingEnabled;
1078     }
1079 
1080     /**
1081      * Indicates whether the form should be validated for dirtyness
1082      *
1083      * <p>
1084      * For FormView, it's necessary to validate when the user tries to navigate out of the form. If set, all the
1085      * AttributeFields will be validated on refresh, navigate, cancel or close Action or on form
1086      * unload and if dirty, displays a message and user can decide whether to continue with
1087      * the action or stay on the form. For lookup and inquiry, it's not needed to validate.
1088      * </p>
1089      *
1090      * @return true if dirty validation is set
1091      */
1092     public boolean isValidateDirty() {
1093         return this.validateDirty;
1094     }
1095 
1096     /**
1097      * Setter for dirty validation.
1098      */
1099     public void setValidateDirty(boolean validateDirty) {
1100         this.validateDirty = validateDirty;
1101     }
1102 
1103     /**
1104      * Indicates whether the Name of the Code should be displayed when a property is of type <code>KualiCode</code>
1105      *
1106      * @param translateCodes - indicates whether <code>KualiCode</code>'s name should be included
1107      */
1108     public void setTranslateCodes(boolean translateCodes) {
1109         this.translateCodes = translateCodes;
1110     }
1111 
1112     /**
1113      * Returns whether the current view supports displaying <code>KualiCode</code>'s name as additional display value
1114      *
1115      * @return true if the current view supports
1116      */
1117     public boolean isTranslateCodes() {
1118         return translateCodes;
1119     }
1120 
1121     /**
1122      * The property name to be used to determine what will be used in the
1123      * breadcrumb title of this view
1124      *
1125      * <p>
1126      * The title can be determined from a combination of this and viewLabelFieldbindingInfo: If only
1127      * viewLabelFieldPropertyName is set, the title we be determined against the
1128      * defaultBindingObjectPath. If only viewLabelFieldbindingInfo is set it
1129      * must provide information about what object(bindToForm or explicit) and
1130      * path to use. If both viewLabelFieldbindingInfo and viewLabelFieldPropertyName are set,
1131      * the bindingInfo will be used with a
1132      * the viewLabelFieldPropertyName as its bindingPath. If neither are set,
1133      * the default title attribute from the dataObject's metadata (determined by the
1134      * defaultBindingObjectPath's object) will be used.
1135      * </p>
1136      *
1137      * @return String property name whose value should be displayed in view label
1138      */
1139     public String getViewLabelFieldPropertyName() {
1140         return this.viewLabelFieldPropertyName;
1141     }
1142 
1143     /**
1144      * Setter for the view label property name
1145      *
1146      * @param viewLabelFieldPropertyName the viewLabelFieldPropertyName to set
1147      */
1148     public void setViewLabelFieldPropertyName(String viewLabelFieldPropertyName) {
1149         this.viewLabelFieldPropertyName = viewLabelFieldPropertyName;
1150     }
1151 
1152     /**
1153      * The option to use when appending the view label on the breadcrumb title.
1154      * Available options: 'dash', 'parenthesis', and 'replace'(don't append -
1155      * simply replace the title). MUST be set for the viewLabelField to be used
1156      * in the breadcrumb, if not set no appendage will be added.
1157      *
1158      * @return the appendOption
1159      */
1160     public String getAppendOption() {
1161         return this.appendOption;
1162     }
1163 
1164     /**
1165      * Setter for the append option
1166      *
1167      * @param appendOption the appendOption to set
1168      * @see View#setViewLabelFieldPropertyName(String)
1169      * @see View#setViewLabelFieldBindingInfo(BindingInfo)
1170      */
1171     public void setAppendOption(String appendOption) {
1172         this.appendOption = appendOption;
1173     }
1174 
1175     /**
1176      * Map of key name/value pairs that will be exposed on the client with JavaScript
1177      *
1178      * <p>
1179      * Any state contained in the Map will be in addition to general state added by the
1180      * <code>ViewHelperService</code> and also state generated from the component properties
1181      * annotated with <code>ClientSideState</code>. If this map does contain a key that is
1182      * the same as the generated state, it will override the generated, with the exception
1183      * of keys that refer to component ids and have a nested map as value, which will be merged
1184      * </p>
1185      *
1186      * @return Map<String, Object> contains key name/value pairs to expose on client
1187      */
1188     public Map<String, Object> getClientSideState() {
1189         return clientSideState;
1190     }
1191 
1192     /**
1193      * Setter for the client side state map
1194      *
1195      * @param clientSideState
1196      */
1197     public void setClientSideState(Map<String, Object> clientSideState) {
1198         this.clientSideState = clientSideState;
1199     }
1200 
1201     /**
1202      * Adds a variable name/value pair to the client side state map associated with the given
1203      * component id
1204      *
1205      * @param componentId - id of the component the state is associated with
1206      * @param variableName - name to expose the state as
1207      * @param value - initial value for the variable on the client
1208      */
1209     public void addToClientSideState(String componentId, String variableName, Object value) {
1210         Map<String, Object> componentClientState = new HashMap<String, Object>();
1211 
1212         // find any existing client state for component
1213         if (clientSideState.containsKey(componentId)) {
1214             Object clientState = clientSideState.get(componentId);
1215             if ((clientState != null) && (clientState instanceof Map)) {
1216                 componentClientState = (Map<String, Object>) clientState;
1217             } else {
1218                 throw new IllegalArgumentException("Client side state for component: " + componentId + " is not a Map");
1219             }
1220         }
1221 
1222         // add variables to component state and reinsert into view's client state
1223         componentClientState.put(variableName, value);
1224         clientSideState.put(componentId, componentClientState);
1225     }
1226 
1227     /**
1228      * Script that is executed at the beginning of page load (before any other script)
1229      *
1230      * <p>
1231      * Many used to set server variables client side
1232      * </p>
1233      *
1234      * @return String pre load script
1235      */
1236     public String getPreLoadScript() {
1237         return preLoadScript;
1238     }
1239 
1240     /**
1241      * Setter for the pre load script
1242      *
1243      * @param preLoadScript
1244      */
1245     public void setPreLoadScript(String preLoadScript) {
1246         this.preLoadScript = preLoadScript;
1247     }
1248 }