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.datadictionary.state.StateMapping;
20 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
21 import org.kuali.rice.krad.uif.UifConstants;
22 import org.kuali.rice.krad.uif.UifConstants.ViewStatus;
23 import org.kuali.rice.krad.uif.UifConstants.ViewType;
24 import org.kuali.rice.krad.uif.container.Container;
25 import org.kuali.rice.krad.uif.container.ContainerBase;
26 import org.kuali.rice.krad.uif.container.Group;
27 import org.kuali.rice.krad.uif.container.NavigationGroup;
28 import org.kuali.rice.krad.uif.container.PageGroup;
29 import org.kuali.rice.krad.uif.component.Component;
30 import org.kuali.rice.krad.uif.component.ReferenceCopy;
31 import org.kuali.rice.krad.uif.component.RequestParameter;
32 import org.kuali.rice.krad.uif.element.Header;
33 import org.kuali.rice.krad.uif.element.Link;
34 import org.kuali.rice.krad.uif.layout.LayoutManager;
35 import org.kuali.rice.krad.uif.service.ViewHelperService;
36 import org.kuali.rice.krad.uif.util.BooleanMap;
37 import org.kuali.rice.krad.uif.util.ClientValidationUtils;
38 import org.kuali.rice.krad.uif.widget.BreadCrumbs;
39 import org.kuali.rice.krad.uif.widget.Growls;
40 import org.kuali.rice.krad.util.ObjectUtils;
41 import org.kuali.rice.krad.web.form.UifFormBase;
42
43 import java.util.ArrayList;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Set;
49
50 /**
51 * Root of the component tree which encompasses a set of related
52 * <code>GroupContainer</code> instances tied together with a common page layout
53 * and navigation.
54 *
55 * <p>
56 * The <code>View</code> component ties together all the components and
57 * configuration of the User Interface for a piece of functionality. In Rice
58 * applications the view is typically associated with a <code>Document</code>
59 * instance.
60 * </p>
61 *
62 * <p>
63 * The view template lays out the common header, footer, and navigation for the
64 * related pages. In addition the view renders the HTML head element bringing in
65 * common script files and style sheets, along with optionally rendering a form
66 * element for pages that need to post data back to the server.
67 * </p>
68 *
69 * <p>
70 * Configuration of UIF features such as model validation is also done through
71 * the <code>View</code>
72 * </p>
73 *
74 * @author Kuali Rice Team (rice.collab@kuali.org)
75 */
76 public class View extends ContainerBase {
77 private static final long serialVersionUID = -1220009725554576953L;
78
79 private String namespaceCode;
80 private String viewName;
81 private ViewTheme theme;
82
83 private int idSequence;
84
85 private String stateObjectBindingPath;
86 private StateMapping stateMapping;
87
88 // application
89 private Header applicationHeader;
90 private Group applicationFooter;
91
92 // Breadcrumbs
93 private BreadCrumbs breadcrumbs;
94 private String breadcrumbTitlePropertyName;
95 private String breadcrumbTitleDisplayOption;
96
97 private boolean renderBreadcrumbsInView;
98
99 // Growls support
100 private Growls growls;
101 private boolean growlMessagingEnabled;
102
103 private String entryPageId;
104
105 @RequestParameter
106 private String currentPageId;
107
108 private NavigationGroup navigation;
109
110 private Class<?> formClass;
111 private String defaultBindingObjectPath;
112 private Map<String, Class<?>> objectPathToConcreteClassMapping;
113
114 private List<String> additionalScriptFiles;
115 private List<String> additionalCssFiles;
116
117 private ViewType viewTypeName;
118
119 private String viewStatus;
120 private ViewIndex viewIndex;
121 private Map<String, String> viewRequestParameters;
122
123 private boolean persistFormToSession;
124
125 private ViewPresentationController presentationController;
126 private ViewAuthorizer authorizer;
127
128 private BooleanMap actionFlags;
129 private BooleanMap editModes;
130
131 private Map<String, String> expressionVariables;
132
133 private boolean singlePageView;
134 private PageGroup page;
135
136 private List<? extends Group> items;
137 private List<? extends Group> dialogs;
138
139 private Link viewMenuLink;
140 private String viewMenuGroupName;
141
142 private boolean applyDirtyCheck;
143 private boolean translateCodesOnReadOnlyDisplay;
144 private boolean supportsRequestOverrideOfReadOnlyFields;
145
146 private String preLoadScript;
147 private Map<String, Object> clientSideState;
148
149 private int preloadPoolSize;
150
151 private Class<? extends ViewHelperService> viewHelperServiceClass;
152
153 @ReferenceCopy
154 private ViewHelperService viewHelperService;
155
156 public View() {
157 singlePageView = false;
158 translateCodesOnReadOnlyDisplay = false;
159 viewTypeName = ViewType.DEFAULT;
160 viewStatus = UifConstants.ViewStatus.CREATED;
161 formClass = UifFormBase.class;
162 renderBreadcrumbsInView = true;
163 supportsRequestOverrideOfReadOnlyFields = true;
164 persistFormToSession = true;
165
166 idSequence = 0;
167 this.viewIndex = new ViewIndex();
168 preloadPoolSize = 0;
169
170 additionalScriptFiles = new ArrayList<String>();
171 additionalCssFiles = new ArrayList<String>();
172 items = new ArrayList<Group>();
173 objectPathToConcreteClassMapping = new HashMap<String, Class<?>>();
174 viewRequestParameters = new HashMap<String, String>();
175 expressionVariables = new HashMap<String, String>();
176 clientSideState = new HashMap<String, Object>();
177
178 dialogs = new ArrayList<Group>();
179 }
180
181 /**
182 * The following initialization is performed:
183 *
184 * <ul>
185 * <li>If a single paged view, set items in page group and put the page in
186 * the items list</li>
187 * </ul>
188 *
189 * @see org.kuali.rice.krad.uif.container.ContainerBase#performInitialization(View, java.lang.Object)
190 */
191 @SuppressWarnings("unchecked")
192 @Override
193 public void performInitialization(View view, Object model) {
194 super.performInitialization(view, model);
195
196 // populate items on page for single paged view
197 if (singlePageView) {
198 if (page != null) {
199 page.setItems(new ArrayList<Group>(items));
200
201 // reset the items list to include the one page
202 items = new ArrayList<Group>();
203 ((List<Group>) items).add(page);
204 } else {
205 throw new RuntimeException("For single paged views the page Group must be set.");
206 }
207 }
208
209 // make sure all the pages have ids before selecting the current page
210 for (Group group : this.getItems()) {
211 if (StringUtils.isBlank(group.getId())) {
212 group.setId(view.getNextId());
213 }
214 }
215 }
216
217 /**
218 * The following updates are done here:
219 *
220 * <ul>
221 * <li>Invoke expression evaluation on view theme</li>
222 * </ul>
223 */
224 public void performApplyModel(View view, Object model, Component parent) {
225 super.performApplyModel(view, model, parent);
226
227 if (theme != null) {
228 KRADServiceLocatorWeb.getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, theme, model,
229 getContext());
230 }
231 }
232
233 /**
234 * The following is performed:
235 *
236 * <ul>
237 * <li>Adds to its document ready script the setupValidator js function for setting
238 * up the validator for this view</li>
239 * </ul>
240 *
241 * @see org.kuali.rice.krad.uif.container.ContainerBase#performFinalize(View,
242 * java.lang.Object, org.kuali.rice.krad.uif.component.Component)
243 */
244 @Override
245 public void performFinalize(View view, Object model, Component parent) {
246 super.performFinalize(view, model, parent);
247
248 String prefixScript = "";
249 if (this.getOnDocumentReadyScript() != null) {
250 prefixScript = this.getPreLoadScript();
251 }
252
253 String growlScript = "";
254 Growls gw = view.getGrowls();
255 if (!gw.getTemplateOptions().isEmpty()) {
256 growlScript = "setGrowlDefaults(" + gw.getTemplateOptionsJSString() + ");";
257 }
258
259 this.setPreLoadScript(prefixScript + growlScript);
260
261 prefixScript = "";
262 if (this.getOnDocumentReadyScript() != null) {
263 prefixScript = this.getOnDocumentReadyScript();
264 }
265
266 this.setOnDocumentReadyScript(prefixScript + "jQuery.extend(jQuery.validator.messages, " +
267 ClientValidationUtils.generateValidatorMessagesOption() + ");");
268 }
269
270 /**
271 * Assigns an id to the component if one was not configured
272 *
273 * @param component - component instance to assign id to
274 */
275 public void assignComponentIds(Component component) {
276 if (component == null) {
277 return;
278 }
279
280 Integer currentSequenceVal = idSequence;
281
282 // assign ID if necessary
283 if (StringUtils.isBlank(component.getId())) {
284 component.setId(UifConstants.COMPONENT_ID_PREFIX + getNextId());
285 }
286
287 // capture current sequence value for component refreshes
288 getViewIndex().addSequenceValueToSnapshot(component.getId(), currentSequenceVal);
289
290 if (component instanceof Container) {
291 LayoutManager layoutManager = ((Container) component).getLayoutManager();
292 if ((layoutManager != null) && StringUtils.isBlank(layoutManager.getId())) {
293 layoutManager.setId(UifConstants.COMPONENT_ID_PREFIX + getNextId());
294 }
295 }
296
297 // assign id to nested components
298 List<Component> allNested = new ArrayList<Component>(component.getComponentsForLifecycle());
299 allNested.addAll(component.getComponentPrototypes());
300 for (Component nestedComponent : allNested) {
301 assignComponentIds(nestedComponent);
302 }
303 }
304
305 /**
306 * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
307 */
308 @Override
309 public List<Component> getComponentsForLifecycle() {
310 List<Component> components = new ArrayList<Component>();
311
312 components.add(applicationHeader);
313 components.add(applicationFooter);
314 components.add(navigation);
315 components.add(breadcrumbs);
316 components.add(growls);
317 components.addAll(dialogs);
318 components.add(viewMenuLink);
319
320 // Note super items should be added after navigation and other view components so
321 // conflicting ids between nav and page do not occur on page navigation via ajax
322 components.addAll(super.getComponentsForLifecycle());
323
324 // remove all pages that are not the current page
325 if (!singlePageView) {
326 for (Group group : this.getItems()) {
327 if ((group instanceof PageGroup) && !StringUtils.equals(group.getId(), getCurrentPageId()) && components
328 .contains(group)) {
329 components.remove(group);
330 }
331 }
332 }
333
334 return components;
335 }
336
337 /**
338 * @see org.kuali.rice.krad.uif.component.Component#getComponentPrototypes()
339 */
340 @Override
341 public List<Component> getComponentPrototypes() {
342 List<Component> components = super.getComponentPrototypes();
343
344 components.add(page);
345
346 return components;
347 }
348
349 /**
350 * @see org.kuali.rice.krad.uif.container.Container#getSupportedComponents()
351 */
352 @Override
353 public Set<Class<? extends Component>> getSupportedComponents() {
354 Set<Class<? extends Component>> supportedComponents = new HashSet<Class<? extends Component>>();
355 supportedComponents.add(Group.class);
356
357 return supportedComponents;
358 }
359
360 /**
361 * @see org.kuali.rice.krad.uif.component.Component#getComponentTypeName()
362 */
363 @Override
364 public String getComponentTypeName() {
365 return "view";
366 }
367
368 /**
369 * Iterates through the contained page items and returns the Page that
370 * matches the set current page id
371 *
372 * @return Page instance
373 */
374 public PageGroup getCurrentPage() {
375 for (Group pageGroup : this.getItems()) {
376 if (StringUtils.equals(pageGroup.getId(), getCurrentPageId()) && pageGroup instanceof PageGroup) {
377 return (PageGroup) pageGroup;
378 }
379 }
380
381 return null;
382 }
383
384 /**
385 * Namespace code the view should be associated with
386 *
387 * <p>
388 * The namespace code is used within the framework in such places as permission checks and parameter
389 * retrieval
390 * </p>
391 *
392 * @return String namespace code
393 */
394 public String getNamespaceCode() {
395 return namespaceCode;
396 }
397
398 /**
399 * Setter for the view's namespace code
400 *
401 * @param namespaceCode
402 */
403 public void setNamespaceCode(String namespaceCode) {
404 this.namespaceCode = namespaceCode;
405 }
406
407 /**
408 * View name provides an identifier for a view within a type. That is if a
409 * set of <code>View</code> instances have the same values for the
410 * properties that are used to retrieve them by their type, the name can be
411 * given to further qualify the view that should be retrieved.
412 * <p>
413 * A view type like the <code>LookupView</code> might have several views for
414 * the same object class, but one that is the 'default' lookup and another
415 * that is the 'advanced' lookup. Therefore the name on the first could be
416 * set to 'default', and likewise the name for the second 'advanced'.
417 * </p>
418 *
419 * @return String name of view
420 */
421 public String getViewName() {
422 return this.viewName;
423 }
424
425 /**
426 * Setter for the view's name
427 *
428 * @param viewName
429 */
430 public void setViewName(String viewName) {
431 this.viewName = viewName;
432 }
433
434 /**
435 * Header for the application containing the view
436 *
437 * <p>
438 * When deploying outside a portal, the application header and footer property can be configured to
439 * display a consistent header/footer across all views. Here application logos, menus, login controls
440 * and so on can be rendered.
441 * </p>
442 *
443 * @return HeaderField application header
444 */
445 public Header getApplicationHeader() {
446 return applicationHeader;
447 }
448
449 /**
450 * Setter for the application header
451 *
452 * @param applicationHeader
453 */
454 public void setApplicationHeader(Header applicationHeader) {
455 this.applicationHeader = applicationHeader;
456 }
457
458 /**
459 * Footer for the application containing the view
460 *
461 * <p>
462 * When deploying outside a portal, the application header and footer property can be configured to
463 * display a consistent header/footer across all views. Here such things as application links, copyrights
464 * and so on can be rendered.
465 * </p>
466 *
467 * @return Group application footer
468 */
469 public Group getApplicationFooter() {
470 return applicationFooter;
471 }
472
473 /**
474 * Setter for the application footer
475 *
476 * @param applicationFooter
477 */
478 public void setApplicationFooter(Group applicationFooter) {
479 this.applicationFooter = applicationFooter;
480 }
481
482 /**
483 * Current sequence value for id assignment
484 *
485 * @return int id sequence
486 */
487 public int getIdSequence() {
488 return idSequence;
489 }
490
491 /**
492 * Setter for the current id sequence value
493 *
494 * @param idSequence
495 */
496 public void setIdSequence(int idSequence) {
497 this.idSequence = idSequence;
498 }
499
500 /**
501 * Returns the next unique id available for components within the view instance
502 *
503 * @return String next id available
504 */
505 public String getNextId() {
506 idSequence += 1;
507 return Integer.toString(idSequence);
508 }
509
510 /**
511 * Specifies what page should be rendered by default. This is the page that
512 * will be rendered when the <code>View</code> is first rendered or when the
513 * current page is not set
514 *
515 * @return String id of the page to render by default
516 */
517 public String getEntryPageId() {
518 return this.entryPageId;
519 }
520
521 /**
522 * Setter for default Page id
523 *
524 * @param entryPageId
525 */
526 public void setEntryPageId(String entryPageId) {
527 this.entryPageId = entryPageId;
528 }
529
530 /**
531 * The id for the page within the view that should be displayed in the UI.
532 * Other pages of the view will not be rendered
533 *
534 * <p>
535 * If current page id is not set, it is set to the configured entry page or first item in list id
536 * </p>
537 *
538 * @return String id of the page that should be displayed
539 */
540 public String getCurrentPageId() {
541 // default current page if not set
542 if (StringUtils.isBlank(currentPageId)) {
543 if (StringUtils.isNotBlank(entryPageId)) {
544 currentPageId = entryPageId;
545 } else if ((getItems() != null) && !getItems().isEmpty()) {
546 Group firstPageGroup = getItems().get(0);
547 currentPageId = firstPageGroup.getId();
548 }
549 }
550
551 return this.currentPageId;
552 }
553
554 /**
555 * Setter for the page id to display
556 *
557 * @param currentPageId
558 */
559 public void setCurrentPageId(String currentPageId) {
560 this.currentPageId = currentPageId;
561 }
562
563 /**
564 * <code>NavigationGroup</code> instance for the <code>View</code>
565 * <p>
566 * Provides configuration necessary to render the navigation. This includes
567 * navigation items in addition to configuration for the navigation
568 * renderer.
569 * </p>
570 *
571 * @return NavigationGroup
572 */
573 public NavigationGroup getNavigation() {
574 return this.navigation;
575 }
576
577 /**
578 * Setter for the View's <code>NavigationGroup</code>
579 *
580 * @param navigation
581 */
582 public void setNavigation(NavigationGroup navigation) {
583 this.navigation = navigation;
584 }
585
586 /**
587 * Class of the Form that should be used with the <code>View</code>
588 * instance. The form is the top level object for all the view's data and is
589 * used to present and accept data in the user interface. All form classes
590 * should extend UifFormBase
591 *
592 * @return Class<?> class for the view's form
593 * @see org.kuali.rice.krad.web.form.UifFormBase
594 */
595 public Class<?> getFormClass() {
596 return this.formClass;
597 }
598
599 /**
600 * Setter for the form class
601 *
602 * @param formClass
603 */
604 public void setFormClass(Class<?> formClass) {
605 this.formClass = formClass;
606 }
607
608 /**
609 * For <code>View</code> types that work primarily with one nested object of
610 * the form (for instance document, or bo) the default binding object path
611 * can be set for each of the views <code>DataBinding</code> components. If
612 * the component does not set its own binding object path it will inherit
613 * the default
614 *
615 * @return String binding path to the object from the form
616 */
617 public String getDefaultBindingObjectPath() {
618 return this.defaultBindingObjectPath;
619 }
620
621 /**
622 * Setter for the default binding object path to use for the view
623 *
624 * @param defaultBindingObjectPath
625 */
626 public void setDefaultBindingObjectPath(String defaultBindingObjectPath) {
627 this.defaultBindingObjectPath = defaultBindingObjectPath;
628 }
629
630 /**
631 * Configures the concrete classes that will be used for properties in the
632 * form object graph that have an abstract or interface type
633 *
634 * <p>
635 * For properties that have an abstract or interface type, it is not
636 * possible to perform operations like getting/settings property values and
637 * getting information from the dictionary. When these properties are
638 * encountered in the object graph, this <code>Map</code> will be consulted
639 * to determine the concrete type to use.
640 * </p>
641 *
642 * <p>
643 * e.g. Suppose we have a property document.accountingLine.accountNumber and
644 * the accountingLine property on the document instance has an interface
645 * type 'AccountingLine'. We can then put an entry into this map with key
646 * 'document.accountingLine', and value
647 * 'org.kuali.rice.sampleapp.TravelAccountingLine'. When getting the
648 * property type or an entry from the dictionary for accountNumber, the
649 * TravelAccountingLine class will be used.
650 * </p>
651 *
652 * @return Map<String, Class> of class implementations keyed by path
653 */
654 public Map<String, Class<?>> getObjectPathToConcreteClassMapping() {
655 return this.objectPathToConcreteClassMapping;
656 }
657
658 /**
659 * Setter for the Map of class implementations keyed by path
660 *
661 * @param objectPathToConcreteClassMapping
662 */
663 public void setObjectPathToConcreteClassMapping(Map<String, Class<?>> objectPathToConcreteClassMapping) {
664 this.objectPathToConcreteClassMapping = objectPathToConcreteClassMapping;
665 }
666
667 /**
668 * Declares additional script files that should be included with the
669 * <code>View</code>. These files are brought into the HTML page along with
670 * common script files configured for the Rice application. Each entry
671 * contain the path to the CSS file, either a relative path, path from web
672 * root, or full URI
673 * <p>
674 * e.g. '/krad/scripts/myScript.js', '../scripts/myScript.js',
675 * 'http://my.edu/web/myScript.js'
676 * </p>
677 *
678 * @return List<String> script file locations
679 */
680 public List<String> getAdditionalScriptFiles() {
681 return this.additionalScriptFiles;
682 }
683
684 /**
685 * Setter for the List of additional script files to included with the
686 * <code>View</code>
687 *
688 * @param additionalScriptFiles
689 */
690 public void setAdditionalScriptFiles(List<String> additionalScriptFiles) {
691 this.additionalScriptFiles = additionalScriptFiles;
692 }
693
694 /**
695 * Declares additional CSS files that should be included with the
696 * <code>View</code>. These files are brought into the HTML page along with
697 * common CSS files configured for the Rice application. Each entry should
698 * contain the path to the CSS file, either a relative path, path from web
699 * root, or full URI
700 * <p>
701 * e.g. '/krad/css/stacked-view.css', '../css/stacked-view.css',
702 * 'http://my.edu/web/stacked-view.css'
703 * </p>
704 *
705 * @return List<String> CSS file locations
706 */
707 public List<String> getAdditionalCssFiles() {
708 return this.additionalCssFiles;
709 }
710
711 /**
712 * Setter for the List of additional CSS files to included with the
713 * <code>View</code>
714 *
715 * @param additionalCssFiles
716 */
717 public void setAdditionalCssFiles(List<String> additionalCssFiles) {
718 this.additionalCssFiles = additionalCssFiles;
719 }
720
721 /**
722 * Specifies the size of the pool that will contain pre-loaded views
723 *
724 * <p>
725 * The spring loading of some views can take a few seconds which hurts performance. The framework supports
726 * pre-loading of view instances so they are available right away when a request is made. This property configures
727 * how many view instances will be pre-loaded. A value of 0 (the default) means no view instances will be
728 * pre-loaded
729 * </p>
730 *
731 * @return int number of view instances to pre-load
732 */
733 public int getPreloadPoolSize() {
734 return preloadPoolSize;
735 }
736
737 /**
738 * Setter for the preloaded view pool size
739 *
740 * @param preloadPoolSize
741 */
742 public void setPreloadPoolSize(int preloadPoolSize) {
743 this.preloadPoolSize = preloadPoolSize;
744 }
745
746 /**
747 * View type name the view is associated with the view instance
748 *
749 * <p>
750 * Views that share common features and functionality can be grouped by the
751 * view type. Usually view types extend the <code>View</code> class to
752 * provide additional configuration and to set defaults. View types can also
753 * implement the <code>ViewTypeService</code> to add special indexing and
754 * retrieval of views.
755 * </p>
756 *
757 * @return String view type name for the view
758 */
759 public ViewType getViewTypeName() {
760 return this.viewTypeName;
761 }
762
763 /**
764 * Setter for the view's type name
765 *
766 * @param viewTypeName
767 */
768 public void setViewTypeName(ViewType viewTypeName) {
769 this.viewTypeName = viewTypeName;
770 }
771
772 /**
773 * Class name of the <code>ViewHelperService</code> that handles the various
774 * phases of the Views lifecycle
775 *
776 * @return Class for the spring bean
777 * @see org.kuali.rice.krad.uif.service.ViewHelperService
778 */
779 public Class<? extends ViewHelperService> getViewHelperServiceClass() {
780 return this.viewHelperServiceClass;
781 }
782
783 /**
784 * Setter for the <code>ViewHelperService</code> class name
785 *
786 * @param viewHelperServiceClass
787 */
788 public void setViewHelperServiceClass(Class<? extends ViewHelperService> viewHelperServiceClass) {
789 this.viewHelperServiceClass = viewHelperServiceClass;
790 }
791
792 /**
793 * Creates the <code>ViewHelperService</code> associated with the View
794 *
795 * @return ViewHelperService instance
796 */
797 public ViewHelperService getViewHelperService() {
798 if ((this.viewHelperService == null) && (this.viewHelperServiceClass != null)) {
799 viewHelperService = ObjectUtils.newInstance(viewHelperServiceClass);
800 }
801
802 return viewHelperService;
803 }
804
805 /**
806 * Invoked to produce a ViewIndex of the current view's components
807 */
808 public void index() {
809 if (this.viewIndex == null) {
810 this.viewIndex = new ViewIndex();
811 }
812 this.viewIndex.index(this);
813 }
814
815 /**
816 * Holds field indexes of the <code>View</code> instance for retrieval
817 *
818 * @return ViewIndex instance
819 */
820 public ViewIndex getViewIndex() {
821 return this.viewIndex;
822 }
823
824 /**
825 * Map of parameters from the request that set view options, used to rebuild
826 * the view on each post
827 * <p>
828 * Views can be configured by parameters. These might impact which parts of
829 * the view are rendered or how the view behaves. Generally these would get
830 * passed in when a new view is requested (by request parameters). These
831 * will be used to initially populate the view properties. In addition, on a
832 * post the view will be rebuilt and properties reset again by the allow
833 * request parameters.
834 * </p>
835 * <p>
836 * Example parameter would be for MaintenaceView whether a New, Edit, or
837 * Copy was requested (maintenance mode)
838 * </p>
839 *
840 * @return
841 */
842 public Map<String, String> getViewRequestParameters() {
843 return this.viewRequestParameters;
844 }
845
846 /**
847 * Setter for the view's request parameters map
848 *
849 * @param viewRequestParameters
850 */
851 public void setViewRequestParameters(Map<String, String> viewRequestParameters) {
852 this.viewRequestParameters = viewRequestParameters;
853 }
854
855 /**
856 * Indicates whether the form (model) associated with the view should be stored in the user session
857 *
858 * <p>
859 * The form class (or model) is used to hold the data that backs the view along with the built view object. Storing
860 * the form instance in session allows many things:
861 *
862 * <ul>
863 * <li>Data does not need to be rebuilt for each server request (for example a collection)</li>
864 * <li>Data that does not need to go to the user can remain on the form, reducing the size of the response and
865 * improving security</li>
866 * <li>Data can be keep around in a 'pre-save' state. When requested by the user changes can then be persisted to
867 * the database</li>
868 * <li>Certain information about the view that was rendered, such as input fields, collection paths, and refresh
869 * components can be kept on the form to support UI interaction</li>
870 * </ul>
871 *
872 * Setting this flag to false will prevent the form from being kept in session and as a result will limit what can
873 * be done by the framework. In almost all cases this is not recommended.
874 * </p>
875 *
876 * <p>
877 * Note all forms will be cleared when the user session expires (based on the rice configuration). In addition, the
878 * framework enables clear points on certain actions to remove the form when it is no longer needed
879 * </p>
880 *
881 * @return boolean true if the form should be stored in the user session, false if only request based
882 */
883 public boolean isPersistFormToSession() {
884 return persistFormToSession;
885 }
886
887 /**
888 * Setter for the persist form to session indicator
889 *
890 * @param persistFormToSession
891 */
892 public void setPersistFormToSession(boolean persistFormToSession) {
893 this.persistFormToSession = persistFormToSession;
894 }
895
896 /**
897 * PresentationController that should be used for the <code>View</code> instance
898 *
899 * <p>
900 * The presentation controller is consulted to determine component (group,
901 * field) state such as required, read-only, and hidden. The presentation
902 * controller does not take into account user permissions. The presentation
903 * controller can also output action flags and edit modes that will be set
904 * onto the view instance and can be referred to by conditional expressions
905 * </p>
906 *
907 * @return PresentationController
908 */
909 public ViewPresentationController getPresentationController() {
910 return this.presentationController;
911 }
912
913 /**
914 * Setter for the view's presentation controller
915 *
916 * @param presentationController
917 */
918 public void setPresentationController(ViewPresentationController presentationController) {
919 this.presentationController = presentationController;
920 }
921
922 /**
923 * Setter for the view's presentation controller by class
924 *
925 * @param presentationControllerClass
926 */
927 public void setPresentationControllerClass(
928 Class<? extends ViewPresentationController> presentationControllerClass) {
929 this.presentationController = ObjectUtils.newInstance(presentationControllerClass);
930 }
931
932 /**
933 * Authorizer that should be used for the <code>View</code> instance
934 *
935 * <p>
936 * The authorizer class is consulted to determine component (group, field)
937 * state such as required, read-only, and hidden based on the users
938 * permissions. It typically communicates with the Kuali Identity Management
939 * system to determine roles and permissions. It is used with the
940 * presentation controller and dictionary conditional logic to determine the
941 * final component state. The authorizer can also output action flags and
942 * edit modes that will be set onto the view instance and can be referred to
943 * by conditional expressions
944 * </p>
945 *
946 * @return Authorizer
947 */
948 public ViewAuthorizer getAuthorizer() {
949 return this.authorizer;
950 }
951
952 /**
953 * Setter for the view's authorizer
954 *
955 * @param authorizer
956 */
957 public void setAuthorizer(ViewAuthorizer authorizer) {
958 this.authorizer = authorizer;
959 }
960
961 /**
962 * Setter for the view's authorizer by class
963 *
964 * @param authorizerClass
965 */
966 public void setAuthorizerClass(Class<? extends ViewAuthorizer> authorizerClass) {
967 this.authorizer = ObjectUtils.newInstance(authorizerClass);
968 }
969
970 /**
971 * Map of strings that flag what actions can be taken in the UI
972 * <p>
973 * These can be used in conditional expressions in the dictionary or by
974 * other UI logic
975 * </p>
976 *
977 * @return BooleanMap action flags
978 */
979 public BooleanMap getActionFlags() {
980 return this.actionFlags;
981 }
982
983 /**
984 * Setter for the action flags Map
985 *
986 * @param actionFlags
987 */
988 public void setActionFlags(BooleanMap actionFlags) {
989 this.actionFlags = actionFlags;
990 }
991
992 /**
993 * Map of edit modes that enabled for the view
994 * <p>
995 * These can be used in conditional expressions in the dictionary or by
996 * other UI logic
997 * </p>
998 *
999 * @return BooleanMap edit modes
1000 */
1001 public BooleanMap getEditModes() {
1002 return this.editModes;
1003 }
1004
1005 /**
1006 * Setter for the edit modes Map
1007 *
1008 * @param editModes
1009 */
1010 public void setEditModes(BooleanMap editModes) {
1011 this.editModes = editModes;
1012 }
1013
1014 /**
1015 * Map that contains expressions to evaluate and make available as variables
1016 * for conditional expressions within the view
1017 * <p>
1018 * Each Map entry contains one expression variables, where the map key gives
1019 * the name for the variable, and the map value gives the variable
1020 * expression. The variables expressions will be evaluated before
1021 * conditional logic is run and made available as variables for other
1022 * conditional expressions. Variable expressions can be based on the model
1023 * and any object contained in the view's context
1024 * </p>
1025 *
1026 * @return Map<String, String> variable expressions
1027 */
1028 public Map<String, String> getExpressionVariables() {
1029 return this.expressionVariables;
1030 }
1031
1032 /**
1033 * Setter for the view's map of variable expressions
1034 *
1035 * @param expressionVariables
1036 */
1037 public void setExpressionVariables(Map<String, String> expressionVariables) {
1038 this.expressionVariables = expressionVariables;
1039 }
1040
1041 /**
1042 * Indicates whether the <code>View</code> only has a single page
1043 * <code>Group</code> or contains multiple page <code>Group</code>
1044 * instances. In the case of a single page it is assumed the group's items
1045 * list contains the section groups for the page, and the page itself is
1046 * given by the page property ({@link #getPage()}. This is for convenience
1047 * of configuration and also can drive other configuration like styling.
1048 *
1049 * @return boolean true if the view only contains one page group, false if
1050 * it contains multple pages
1051 */
1052 public boolean isSinglePageView() {
1053 return this.singlePageView;
1054 }
1055
1056 /**
1057 * Setter for the single page indicator
1058 *
1059 * @param singlePageView
1060 */
1061 public void setSinglePageView(boolean singlePageView) {
1062 this.singlePageView = singlePageView;
1063 }
1064
1065 /**
1066 * For single paged views ({@link #isSinglePageView()}, gives the page
1067 * <code>Group</code> the view should render. The actual items for the page
1068 * is taken from the group's items list ({@link #getItems()}, and set onto
1069 * the give page group. This is for convenience of configuration.
1070 *
1071 * @return Group page group for single page views
1072 */
1073 public PageGroup getPage() {
1074 return this.page;
1075 }
1076
1077 /**
1078 * Setter for the page group for single page views
1079 *
1080 * @param page
1081 */
1082 public void setPage(PageGroup page) {
1083 this.page = page;
1084 }
1085
1086 /**
1087 * @see org.kuali.rice.krad.uif.container.ContainerBase#getItems()
1088 */
1089 @Override
1090 public List<? extends Group> getItems() {
1091 return this.items;
1092 }
1093
1094 /**
1095 * Setter for the view's <code>Group</code> instances
1096 *
1097 * @param items
1098 */
1099 @Override
1100 public void setItems(List<? extends Component> items) {
1101 // TODO: fix this generic issue
1102 this.items = (List<? extends Group>) items;
1103 }
1104
1105 /**
1106 * Provide a list of dialog groups associated with this view
1107 *
1108 * @return List of dialog Groups
1109 */
1110 public List<? extends Group> getDialogs() {
1111 return dialogs;
1112 }
1113
1114 /**
1115 * Sets the list of dialog groups for this view
1116 *
1117 * @param dialogs - List of dialog groups
1118 */
1119 public void setDialogs(List<? extends Group> dialogs) {
1120 this.dialogs = dialogs;
1121 }
1122
1123 /**
1124 * Provides configuration for displaying a link to the view from an
1125 * application menu
1126 *
1127 * @return Link view link field
1128 */
1129 public Link getViewMenuLink() {
1130 return this.viewMenuLink;
1131 }
1132
1133 /**
1134 * Setter for the views link field
1135 *
1136 * @param viewMenuLink
1137 */
1138 public void setViewMenuLink(Link viewMenuLink) {
1139 this.viewMenuLink = viewMenuLink;
1140 }
1141
1142 /**
1143 * Provides a grouping string for the view to group its menu link (within a
1144 * portal for instance)
1145 *
1146 * @return String menu grouping
1147 */
1148 public String getViewMenuGroupName() {
1149 return this.viewMenuGroupName;
1150 }
1151
1152 /**
1153 * Setter for the views menu grouping
1154 *
1155 * @param viewMenuGroupName
1156 */
1157 public void setViewMenuGroupName(String viewMenuGroupName) {
1158 this.viewMenuGroupName = viewMenuGroupName;
1159 }
1160
1161 /**
1162 * Indicates what lifecycle phase the View instance is in
1163 * <p>
1164 * The view lifecycle begins with the CREATED status. In this status a new
1165 * instance of the view has been retrieved from the dictionary, but no
1166 * further processing has been done. After the initialize phase has been run
1167 * the status changes to INITIALIZED. After the model has been applied and
1168 * the view is ready for render the status changes to FINAL
1169 * </p>
1170 *
1171 * @return String view status
1172 * @see org.kuali.rice.krad.uif.UifConstants.ViewStatus
1173 */
1174 public String getViewStatus() {
1175 return this.viewStatus;
1176 }
1177
1178 /**
1179 * Setter for the view status
1180 *
1181 * @param viewStatus
1182 */
1183 public void setViewStatus(String viewStatus) {
1184 this.viewStatus = viewStatus;
1185 }
1186
1187 /**
1188 * Indicates whether the view has been initialized
1189 *
1190 * @return boolean true if the view has been initialized, false if not
1191 */
1192 public boolean isInitialized() {
1193 return StringUtils.equals(viewStatus, ViewStatus.INITIALIZED) ||
1194 StringUtils.equals(viewStatus, ViewStatus.FINAL);
1195 }
1196
1197 /**
1198 * Indicates whether the view has been updated from the model and final
1199 * updates made
1200 *
1201 * @return boolean true if the view has been updated, false if not
1202 */
1203 public boolean isFinal() {
1204 return StringUtils.equals(viewStatus, ViewStatus.FINAL);
1205 }
1206
1207 /**
1208 * Breadcrumb widget used for displaying homeward path and history
1209 *
1210 * @return the breadcrumbs
1211 */
1212 public BreadCrumbs getBreadcrumbs() {
1213 return this.breadcrumbs;
1214 }
1215
1216 /**
1217 * @param breadcrumbs the breadcrumbs to set
1218 */
1219 public void setBreadcrumbs(BreadCrumbs breadcrumbs) {
1220 this.breadcrumbs = breadcrumbs;
1221 }
1222
1223 /**
1224 * Indicates whether the breadcrumbs should be rendered in the view or if they have been rendered in
1225 * the application header
1226 *
1227 * <p>
1228 * For layout purposes it is sometimes necessary to render the breadcrumbs in the application header. This flag
1229 * indicates that is being done (by setting to false) and therefore should not be rendered in the view template.
1230 * </p>
1231 *
1232 * @return boolean true if breadcrumbs should be rendered in the view, false if not (are rendered in the
1233 * application header)
1234 */
1235 public boolean isRenderBreadcrumbsInView() {
1236 return renderBreadcrumbsInView;
1237 }
1238
1239 /**
1240 * Setter for the render breadcrumbs in view indicator
1241 *
1242 * @param renderBreadcrumbsInView
1243 */
1244 public void setRenderBreadcrumbsInView(boolean renderBreadcrumbsInView) {
1245 this.renderBreadcrumbsInView = renderBreadcrumbsInView;
1246 }
1247
1248 /**
1249 * Growls widget which sets up global settings for the growls used in this
1250 * view and its pages
1251 *
1252 * @return the growls
1253 */
1254 public Growls getGrowls() {
1255 return this.growls;
1256 }
1257
1258 /**
1259 * @param growls the growls to set
1260 */
1261 public void setGrowls(Growls growls) {
1262 this.growls = growls;
1263 }
1264
1265 /**
1266 * whether to use growls to show messages - info, warning and error
1267 *
1268 * <p>Growls use the messages contained in the message map. If enabled, info
1269 * messages in their entirety will be displayed in growls, for warning and
1270 * error messages a growl message will notify the user that these messages
1271 * exist on the page.</p>
1272 *
1273 * <p> If this setting is disabled, it is recommended that
1274 * infoMessage display be enabled for the page ValidationMessages bean in order to
1275 * display relevant information to the user. Note: the growl scripts are
1276 * built out in the PageGroup class.</p>
1277 *
1278 * @return the growlMessagingEnabled
1279 */
1280 public boolean isGrowlMessagingEnabled() {
1281 return this.growlMessagingEnabled;
1282 }
1283
1284 /**
1285 * enable or disable showing of messages using growls
1286 *
1287 * @param growlMessagingEnabled the growlMessagingEnabled to set
1288 */
1289 public void setGrowlMessagingEnabled(boolean growlMessagingEnabled) {
1290 this.growlMessagingEnabled = growlMessagingEnabled;
1291 }
1292
1293 /**
1294 * Indicates whether the form should be validated for dirtyness
1295 *
1296 * <p>
1297 * For FormView, it's necessary to validate when the user tries to navigate out of the form. If set, all the
1298 * InputFields will be validated on refresh, navigate, cancel or close Action or on form
1299 * unload and if dirty, displays a message and user can decide whether to continue with
1300 * the action or stay on the form. For lookup and inquiry, it's not needed to validate.
1301 * </p>
1302 *
1303 * @return true if dirty validation is set
1304 */
1305 public boolean isApplyDirtyCheck() {
1306 return this.applyDirtyCheck;
1307 }
1308
1309 /**
1310 * Setter for dirty validation.
1311 */
1312 public void setApplyDirtyCheck(boolean applyDirtyCheck) {
1313 this.applyDirtyCheck = applyDirtyCheck;
1314 }
1315
1316 /**
1317 * Indicates whether the Name of the Code should be displayed when a property is of type <code>KualiCode</code>
1318 *
1319 * @param translateCodesOnReadOnlyDisplay - indicates whether <code>KualiCode</code>'s name should be included
1320 */
1321 public void setTranslateCodesOnReadOnlyDisplay(boolean translateCodesOnReadOnlyDisplay) {
1322 this.translateCodesOnReadOnlyDisplay = translateCodesOnReadOnlyDisplay;
1323 }
1324
1325 /**
1326 * Returns whether the current view supports displaying <code>KualiCode</code>'s name as additional display value
1327 *
1328 * @return true if the current view supports
1329 */
1330 public boolean isTranslateCodesOnReadOnlyDisplay() {
1331 return translateCodesOnReadOnlyDisplay;
1332 }
1333
1334 /**
1335 * The property name to be used to determine what will be used in the
1336 * breadcrumb title of this view
1337 *
1338 * <p>
1339 * The title can be determined from a combination of this and viewLabelFieldbindingInfo: If only
1340 * viewLabelFieldPropertyName is set, the title we be determined against the
1341 * defaultBindingObjectPath. If only viewLabelFieldbindingInfo is set it
1342 * must provide information about what object(bindToForm or explicit) and
1343 * path to use. If both viewLabelFieldbindingInfo and viewLabelFieldPropertyName are set,
1344 * the bindingInfo will be used with a
1345 * the viewLabelFieldPropertyName as its bindingPath. If neither are set,
1346 * the default title attribute from the dataObject's metadata (determined by the
1347 * defaultBindingObjectPath's object) will be used.
1348 * </p>
1349 *
1350 * @return String property name whose value should be displayed in view label
1351 */
1352 public String getBreadcrumbTitlePropertyName() {
1353 return this.breadcrumbTitlePropertyName;
1354 }
1355
1356 /**
1357 * Setter for the view label property name
1358 *
1359 * @param breadcrumbTitlePropertyName the viewLabelFieldPropertyName to set
1360 */
1361 public void setBreadcrumbTitlePropertyName(String breadcrumbTitlePropertyName) {
1362 this.breadcrumbTitlePropertyName = breadcrumbTitlePropertyName;
1363 }
1364
1365 /**
1366 * The option to use when appending the view label on the breadcrumb title.
1367 * Available options: 'dash', 'parenthesis', and 'replace'(don't append -
1368 * simply replace the title). MUST be set for the viewLabelField to be used
1369 * in the breadcrumb, if not set no appendage will be added.
1370 *
1371 * @return the appendOption
1372 */
1373 public String getBreadcrumbTitleDisplayOption() {
1374 return this.breadcrumbTitleDisplayOption;
1375 }
1376
1377 /**
1378 * Setter for the append option
1379 *
1380 * @param breadcrumbTitleDisplayOption the appendOption to set
1381 */
1382 public void setBreadcrumbTitleDisplayOption(String breadcrumbTitleDisplayOption) {
1383 this.breadcrumbTitleDisplayOption = breadcrumbTitleDisplayOption;
1384 }
1385
1386 /**
1387 * Map of key name/value pairs that will be exposed on the client with JavaScript
1388 *
1389 * <p>
1390 * Any state contained in the Map will be in addition to general state added by the
1391 * <code>ViewHelperService</code> and also state generated from the component properties
1392 * annotated with <code>ClientSideState</code>. If this map does contain a key that is
1393 * the same as the generated state, it will override the generated, with the exception
1394 * of keys that refer to component ids and have a nested map as value, which will be merged
1395 * </p>
1396 *
1397 * @return Map<String, Object> contains key name/value pairs to expose on client
1398 */
1399 public Map<String, Object> getClientSideState() {
1400 return clientSideState;
1401 }
1402
1403 /**
1404 * Setter for the client side state map
1405 *
1406 * @param clientSideState
1407 */
1408 public void setClientSideState(Map<String, Object> clientSideState) {
1409 this.clientSideState = clientSideState;
1410 }
1411
1412 /**
1413 * Adds a variable name/value pair to the client side state map associated with the given
1414 * component id
1415 *
1416 * @param componentId - id of the component the state is associated with
1417 * @param variableName - name to expose the state as
1418 * @param value - initial value for the variable on the client
1419 */
1420 public void addToClientSideState(String componentId, String variableName, Object value) {
1421 Map<String, Object> componentClientState = new HashMap<String, Object>();
1422
1423 // find any existing client state for component
1424 if (clientSideState.containsKey(componentId)) {
1425 Object clientState = clientSideState.get(componentId);
1426 if ((clientState != null) && (clientState instanceof Map)) {
1427 componentClientState = (Map<String, Object>) clientState;
1428 } else {
1429 throw new IllegalArgumentException("Client side state for component: " + componentId + " is not a Map");
1430 }
1431 }
1432
1433 // add variables to component state and reinsert into view's client state
1434 componentClientState.put(variableName, value);
1435 clientSideState.put(componentId, componentClientState);
1436 }
1437
1438 /**
1439 * Indicates whether the view allows read only fields to be specified on the request URL which will
1440 * override the view setting
1441 *
1442 * <p>
1443 * If enabled, the readOnlyFields request parameter can be sent to indicate fields that should be set read only
1444 * </p>
1445 *
1446 * @return boolean true if read only request overrides are allowed, false if not
1447 */
1448 public boolean isSupportsRequestOverrideOfReadOnlyFields() {
1449 return supportsRequestOverrideOfReadOnlyFields;
1450 }
1451
1452 /**
1453 * Setter for the the read only field override indicator
1454 *
1455 * @param supportsRequestOverrideOfReadOnlyFields
1456 */
1457 public void setSupportsRequestOverrideOfReadOnlyFields(boolean supportsRequestOverrideOfReadOnlyFields) {
1458 this.supportsRequestOverrideOfReadOnlyFields = supportsRequestOverrideOfReadOnlyFields;
1459 }
1460
1461 /**
1462 * Script that is executed at the beginning of page load (before any other script)
1463 *
1464 * <p>
1465 * Many used to set server variables client side
1466 * </p>
1467 *
1468 * @return String pre load script
1469 */
1470 public String getPreLoadScript() {
1471 return preLoadScript;
1472 }
1473
1474 /**
1475 * Setter for the pre load script
1476 *
1477 * @param preLoadScript
1478 */
1479 public void setPreLoadScript(String preLoadScript) {
1480 this.preLoadScript = preLoadScript;
1481 }
1482
1483 /**
1484 * The theme which contains stylesheets for this view
1485 * @return
1486 */
1487 public ViewTheme getTheme() {
1488 return theme;
1489 }
1490
1491 /**
1492 * Setter for The theme which contains stylesheets for this view
1493 * @return
1494 */
1495 public void setTheme(ViewTheme theme) {
1496 this.theme = theme;
1497 }
1498
1499 /**
1500 * The stateObject's binding path, this will be used along with the StateMapping's statePropertyName to
1501 * determine what field in the model state information is stored in for this view. Used during View validation.
1502 *
1503 * @return stateObjectBindingPath path to the object storing state information
1504 */
1505 public String getStateObjectBindingPath() {
1506 return stateObjectBindingPath;
1507 }
1508
1509 /**
1510 * The stateObject's binding path, this will be used along with the StateMapping's statePropertyName to
1511 * determine what field in the model state information is stored in for this view. Used during View validation.
1512 *
1513 * @param stateObjectBindingPath
1514 */
1515 public void setStateObjectBindingPath(String stateObjectBindingPath) {
1516 this.stateObjectBindingPath = stateObjectBindingPath;
1517 }
1518
1519 /**
1520 * Gets the stateMapping.
1521 *
1522 * <p>The state mapping object is used to determine the state information for a view,
1523 * it must include an ordered list of states, and where to find the state information for the view.
1524 * A stateMapping must be set for state based validation to occur. When stateMapping information is
1525 * not included, the view's model is considered stateless and all constraints will apply regardless of their
1526 * state information or replacements (ie, they will function as they did in version 2.1).</p>
1527 *
1528 * @since 2.2
1529 * @return StateMapping information needed for state based validation, if null no state based validation functionality
1530 * will exist and configured constraints will apply regardless of state
1531 */
1532 public StateMapping getStateMapping() {
1533 return stateMapping;
1534 }
1535
1536 /**
1537 * Set the stateMapping
1538 *
1539 * @param stateMapping
1540 */
1541 public void setStateMapping(StateMapping stateMapping) {
1542 this.stateMapping = stateMapping;
1543 }
1544 }