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