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