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 }