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