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