001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.uif.view; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.CoreApiServiceLocator; 020import org.kuali.rice.krad.datadictionary.DataDictionary; 021import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 022import org.kuali.rice.krad.datadictionary.state.StateMapping; 023import org.kuali.rice.krad.datadictionary.validator.ValidationTrace; 024import org.kuali.rice.krad.datadictionary.validator.Validator; 025import org.kuali.rice.krad.lookup.LookupView; 026import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 027import org.kuali.rice.krad.uif.UifConstants; 028import org.kuali.rice.krad.uif.UifConstants.ViewStatus; 029import org.kuali.rice.krad.uif.UifConstants.ViewType; 030import org.kuali.rice.krad.uif.UifParameters; 031import org.kuali.rice.krad.uif.UifPropertyPaths; 032import org.kuali.rice.krad.uif.component.BindingInfo; 033import org.kuali.rice.krad.uif.component.Component; 034import org.kuali.rice.krad.uif.component.DelayedCopy; 035import org.kuali.rice.krad.uif.component.ReferenceCopy; 036import org.kuali.rice.krad.uif.component.RequestParameter; 037import org.kuali.rice.krad.uif.container.ContainerBase; 038import org.kuali.rice.krad.uif.container.Group; 039import org.kuali.rice.krad.uif.container.PageGroup; 040import org.kuali.rice.krad.uif.element.BreadcrumbItem; 041import org.kuali.rice.krad.uif.element.BreadcrumbOptions; 042import org.kuali.rice.krad.uif.element.HeadLink; 043import org.kuali.rice.krad.uif.element.Header; 044import org.kuali.rice.krad.uif.element.MetaTag; 045import org.kuali.rice.krad.uif.element.ViewHeader; 046import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 047import org.kuali.rice.krad.uif.lifecycle.ViewLifecyclePhase; 048import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleRestriction; 049import org.kuali.rice.krad.uif.service.ViewHelperService; 050import org.kuali.rice.krad.uif.util.ClientValidationUtils; 051import org.kuali.rice.krad.uif.util.ComponentFactory; 052import org.kuali.rice.krad.uif.util.CopyUtils; 053import org.kuali.rice.krad.uif.util.LifecycleAwareList; 054import org.kuali.rice.krad.uif.util.LifecycleAwareMap; 055import org.kuali.rice.krad.uif.util.LifecycleElement; 056import org.kuali.rice.krad.uif.util.ParentLocation; 057import org.kuali.rice.krad.uif.util.ScriptUtils; 058import org.kuali.rice.krad.uif.widget.BlockUI; 059import org.kuali.rice.krad.uif.widget.Breadcrumbs; 060import org.kuali.rice.krad.uif.widget.Growls; 061import org.kuali.rice.krad.uif.widget.Help; 062import org.kuali.rice.krad.util.GlobalVariables; 063import org.kuali.rice.krad.util.KRADConstants; 064import org.kuali.rice.krad.util.KRADUtils; 065import org.kuali.rice.krad.web.form.UifFormBase; 066import org.slf4j.Logger; 067import org.slf4j.LoggerFactory; 068 069import java.util.ArrayList; 070import java.util.Collections; 071import java.util.HashMap; 072import java.util.HashSet; 073import java.util.List; 074import java.util.Map; 075import java.util.Set; 076 077/** 078 * Root of the component tree which encompasses a set of related 079 * GroupContainer instances tied together with a common page layout 080 * and navigation. 081 * 082 * <p> 083 * The View component ties together all the components and 084 * configuration of the User Interface for a piece of functionality. In Rice 085 * applications the view is typically associated with a Document 086 * instance. 087 * </p> 088 * 089 * <p> 090 * The view template lays out the common header, footer, and navigation for the 091 * related pages. In addition the view renders the HTML head element bringing in 092 * common script files and style sheets, along with optionally rendering a form 093 * element for pages that need to post data back to the server. 094 * </p> 095 * 096 * <p> 097 * Configuration of UIF features such as model validation is also done through 098 * the View 099 * </p> 100 * 101 * @author Kuali Rice Team (rice.collab@kuali.org) 102 */ 103public class View extends ContainerBase { 104 private static final long serialVersionUID = -1220009725554576953L; 105 private static final Logger LOG = LoggerFactory.getLogger(ContainerBase.class); 106 107 private String namespaceCode; 108 private String viewName; 109 private ViewTheme theme; 110 111 private String stateObjectBindingPath; 112 private StateMapping stateMapping; 113 114 // view header setting 115 private boolean unifiedHeader; 116 117 // additional view group(s) 118 private Group topGroup; 119 120 // application 121 private Header applicationHeader; 122 private Group applicationFooter; 123 private Group buildFooter; 124 private String applicationTitleText; 125 126 // sticky flags 127 private boolean stickyTopGroup; 128 private boolean stickyBreadcrumbs; 129 private boolean stickyHeader; 130 private boolean stickyApplicationHeader; 131 private boolean stickyFooter; 132 private boolean stickyApplicationFooter; 133 134 private List<String> contentContainerCssClasses; 135 136 // Breadcrumbs 137 private Breadcrumbs breadcrumbs; 138 private BreadcrumbOptions breadcrumbOptions; 139 private BreadcrumbItem breadcrumbItem; 140 private ParentLocation parentLocation; 141 private List<BreadcrumbItem> pathBasedBreadcrumbs; 142 143 // Growls support 144 private Growls growls; 145 private boolean growlMessagingEnabled; 146 147 private BlockUI refreshBlockUI; 148 private BlockUI navigationBlockUI; 149 150 private String entryPageId; 151 152 @RequestParameter 153 private String currentPageId; 154 155 private Group navigation; 156 157 private Class<?> formClass; 158 private String defaultBindingObjectPath; 159 private Map<String, Class<?>> objectPathToConcreteClassMapping; 160 161 private List<String> additionalScriptFiles; 162 private List<String> additionalCssFiles; 163 private List<HeadLink> additionalHeadLinks; 164 private List<MetaTag> additionalMetaTags; 165 private boolean useLibraryCssClasses; 166 167 private ViewType viewTypeName; 168 169 protected ViewIndex viewIndex; 170 private Map<String, String> viewRequestParameters; 171 172 private boolean persistFormToSession; 173 private ViewSessionPolicy sessionPolicy; 174 175 private int preloadPoolSize; 176 177 private ViewPresentationController presentationController; 178 private ViewAuthorizer authorizer; 179 private Class<? extends RequestAuthorizationCache> requestAuthorizationCacheClass; 180 181 private Map<String, Boolean> actionFlags; 182 private Map<String, Boolean> editModes; 183 184 private Map<String, String> expressionVariables; 185 186 private boolean singlePageView; 187 private boolean mergeWithPageItems; 188 private PageGroup page; 189 190 @ReferenceCopy(referenceTransient=true) 191 private PageGroup currentPage; 192 193 private List<Group> dialogs; 194 195 protected boolean applyDirtyCheck; 196 private boolean translateCodesOnReadOnlyDisplay; 197 private boolean supportsRequestOverrideOfReadOnlyFields; 198 private boolean disableNativeAutocomplete; 199 private boolean disableBrowserCache; 200 201 private String preLoadScript; 202 203 @DelayedCopy 204 private List<? extends Component> items; 205 206 private List<String> viewTemplates; 207 208 private Class<? extends ViewHelperService> viewHelperServiceClass; 209 210 @ReferenceCopy 211 private ViewHelperService viewHelperService; 212 213 private Map<String, Object> preModelContext; 214 215 public View() { 216 singlePageView = false; 217 mergeWithPageItems = true; 218 translateCodesOnReadOnlyDisplay = false; 219 viewTypeName = ViewType.DEFAULT; 220 formClass = UifFormBase.class; 221 supportsRequestOverrideOfReadOnlyFields = true; 222 disableBrowserCache = true; 223 persistFormToSession = true; 224 sessionPolicy = new ViewSessionPolicy(); 225 226 this.viewIndex = new ViewIndex(); 227 228 additionalScriptFiles = Collections.emptyList(); 229 additionalCssFiles = Collections.emptyList(); 230 additionalHeadLinks = Collections.emptyList(); 231 additionalMetaTags = Collections.emptyList(); 232 objectPathToConcreteClassMapping = Collections.emptyMap(); 233 viewRequestParameters = Collections.emptyMap(); 234 expressionVariables = Collections.emptyMap(); 235 236 requestAuthorizationCacheClass = RequestAuthorizationCache.class; 237 238 dialogs = Collections.emptyList(); 239 240 items = Collections.emptyList(); 241 viewTemplates = new LifecycleAwareList<String>(this); 242 } 243 244 /** 245 * The following initialization is performed: 246 * 247 * <ul> 248 * <li>If a single paged view, set items in page group and put the page in 249 * the items list</li> 250 * <li>If {@link ViewSessionPolicy#enableTimeoutWarning} is enabled add the session timeout dialogs to the 251 * views list of dialog groups</li> 252 * </ul> 253 * 254 * {@inheritDoc} 255 */ 256 @SuppressWarnings("unchecked") 257 @Override 258 public void performInitialization(Object model) { 259 if (model instanceof UifFormBase) { 260 UifFormBase form = (UifFormBase) model; 261 262 // set view page to page requested on form 263 if (StringUtils.isNotBlank(form.getPageId())) { 264 setCurrentPageId(form.getPageId()); 265 } 266 267 String dialogId = form.getActionParamaterValue(UifParameters.DIALOG_ID); 268 if (StringUtils.isNotBlank(dialogId)) { 269 form.setShowDialogId(dialogId); 270 271 // initialize the view to open the dialog if necessary 272 initializeDialogLoadScript((UifFormBase) model); 273 } 274 } 275 276 initializeDialogReclocationScript(); 277 278 super.performInitialization(model); 279 280 assert this == ViewLifecycle.getView(); 281 282 // populate items on page for single paged view 283 if (singlePageView) { 284 if (page != null) { 285 // remove default sections of page when requested 286 if (!mergeWithPageItems) { 287 page.setItems(new ArrayList<Group>()); 288 } 289 290 // add the items configured on the view to the page items, and set as the 291 // new page items 292 List<Component> newItems = (List<Component>) page.getItems(); 293 newItems.addAll(items); 294 page.setItems(newItems); 295 296 page.sortItems(); 297 298 // reset the items list to include the one page 299 items = new ArrayList<Group>(); 300 ((List<Group>) items).add(page); 301 } 302 } 303 // if items is only size one and instance of page, set singlePageView to true 304 else if ((this.items != null) && (this.items.size() == 1)) { 305 Component itemComponent = this.items.get(0); 306 307 if (itemComponent instanceof PageGroup) { 308 this.singlePageView = true; 309 } 310 } 311 312 if (sessionPolicy.isEnableTimeoutWarning()) { 313 Group warningDialog = ComponentFactory.getSessionTimeoutWarningDialog(); 314 warningDialog.setId(ComponentFactory.SESSION_TIMEOUT_WARNING_DIALOG); 315 getDialogs().add(warningDialog); 316 317 Group timeoutDialog = ComponentFactory.getSessionTimeoutDialog(); 318 timeoutDialog.setId(ComponentFactory.SESSION_TIMEOUT_DIALOG); 319 getDialogs().add(timeoutDialog); 320 } 321 322 breadcrumbOptions.setupBreadcrumbs(model); 323 } 324 325 /** 326 * Helper method to set the view's load script to open a dialog. 327 * 328 * @param form the form containing data 329 */ 330 protected void initializeDialogLoadScript(UifFormBase form) { 331 // check for edit line dialog action parameters and if present, then show the dialog once page is loaded 332 String selectedCollectionPath = form.getActionParamaterValue(UifParameters.SELECTED_COLLECTION_PATH); 333 String selectedLineIndex = form.getActionParamaterValue(UifParameters.SELECTED_LINE_INDEX); 334 String dialogId = form.getShowDialogId(); 335 336 if (StringUtils.isNotBlank(dialogId) && StringUtils.isNotBlank(selectedCollectionPath) && StringUtils 337 .isNotBlank(selectedLineIndex)) { 338 // the line index and the collection path need adjusted to account for nested collections within a 339 // dialog because the dialog is shown per the root collection, but the index and path are per 340 // the nested sub-collection 341 String originalLineIndex = StringUtils.substring(dialogId, dialogId.length() - 1); 342 if (!selectedLineIndex.equals(originalLineIndex)) { 343 selectedLineIndex = originalLineIndex; 344 } 345 346 // adjust the collection path to point to the root collection rather than the sub-collection 347 if (selectedCollectionPath.contains(UifPropertyPaths.DIALOG_DATA_OBJECT)) { 348 String collectionId = StringUtils.substring(dialogId, dialogId.indexOf("_") + 1, dialogId.lastIndexOf( 349 "_")); 350 BindingInfo bindingInfo = (BindingInfo) form.getViewPostMetadata().getComponentPostMetadata( 351 collectionId).getData("bindingInfo"); 352 selectedCollectionPath = bindingInfo.getBindingPath(); 353 } 354 355 String actionScript = "setupImages();"; 356 if (StringUtils.startsWith(dialogId, ComponentFactory.EDIT_LINE_DIALOG)) { 357 actionScript += 358 "showEditLineDialog('" + dialogId + "', '" + selectedCollectionPath + "', " + selectedLineIndex 359 + ");"; 360 } else { 361 String additionalData = "{ 'actionParameters[selectedCollectionPath]' : '" + selectedCollectionPath 362 + "', 'actionParameters[selectedLineIndex]' : '0' }"; 363 actionScript += "showDialog('" + dialogId + "', " + additionalData + ");"; 364 } 365 setOnLoadScript(ScriptUtils.appendScript(getOnLoadScript(), actionScript)); 366 } else if (StringUtils.isNotBlank(dialogId) && !(this instanceof LookupView)) { 367 String actionScript = 368 "jQuery.unblockUI();setupImages();showLoading('Loading...', window.document);showDialog('" 369 + dialogId + "');"; 370 setOnLoadScript(ScriptUtils.appendScript(getOnLoadScript(), actionScript)); 371 } 372 } 373 374 /** 375 * Without this script dialogs might be associated with a non visible action (i.e. inside a dropdown menu). This 376 * would cause the dialog not being displayed. To ensure that all dialog are able to be displayed the DIVs of the 377 * dialogs are moved to a common location. 378 */ 379 protected void initializeDialogReclocationScript() { 380 String dialogRelocationScript = "jQuery('.modal').appendTo('#Uif-Dialogs');"; 381 setOnLoadScript(ScriptUtils.appendScript(getOnLoadScript(), dialogRelocationScript)); 382 } 383 384 /** 385 * {@inheritDoc} 386 */ 387 @Override 388 public void afterEvaluateExpression() { 389 super.afterEvaluateExpression(); 390 391 if (getReadOnly() == null) { 392 setReadOnly(false); 393 } 394 } 395 396 /** 397 * The following updates are done here: 398 * 399 * <ul> 400 * <li>Invoke expression evaluation on view theme</li> 401 * <li>Invoke theme to configure defaults</li> 402 * </ul> 403 */ 404 @Override 405 public void performApplyModel(Object model, LifecycleElement parent) { 406 super.performApplyModel(model, parent); 407 408 View view = ViewLifecycle.getView(); 409 if (theme != null) { 410 ViewLifecycle.getExpressionEvaluator().evaluateExpressionsOnConfigurable(view, theme, getContext()); 411 412 theme.configureThemeDefaults(); 413 } 414 415 if (sessionPolicy != null) { 416 ViewLifecycle.getExpressionEvaluator().evaluateExpressionsOnConfigurable(view, sessionPolicy, getContext()); 417 } 418 419 //handle parentLocation breadcrumb chain 420 parentLocation.constructParentLocationBreadcrumbItems(view, model, view.getContext()); 421 } 422 423 /** 424 * The following is performed: 425 * 426 * <ul> 427 * <li>Adds to its document ready script the setupValidator js function for setting 428 * up the validator for this view</li> 429 * </ul> 430 * 431 * {@inheritDoc} 432 */ 433 @SuppressWarnings("unchecked") 434 @Override 435 public void performFinalize(Object model, LifecycleElement parent) { 436 super.performFinalize(model, parent); 437 438 assert this == ViewLifecycle.getView(); 439 440 String preLoadScript = ""; 441 if (this.getPreLoadScript() != null) { 442 preLoadScript = this.getPreLoadScript(); 443 } 444 445 // Retrieve Growl and BlockUI settings 446 Growls gw = getGrowls(); 447 if (!gw.getTemplateOptions().isEmpty()) { 448 preLoadScript += "setGrowlDefaults(" + gw.getTemplateOptionsJSString() + ");"; 449 } 450 451 BlockUI navBlockUI = getNavigationBlockUI(); 452 if (!navBlockUI.getTemplateOptions().isEmpty()) { 453 preLoadScript += "setBlockUIDefaults(" 454 + navBlockUI.getTemplateOptionsJSString() 455 + ", '" 456 + UifConstants.BLOCKUI_NAVOPTS 457 + "');"; 458 } 459 460 BlockUI refBlockUI = getRefreshBlockUI(); 461 if (!refBlockUI.getTemplateOptions().isEmpty()) { 462 preLoadScript += "setBlockUIDefaults(" 463 + refBlockUI.getTemplateOptionsJSString() 464 + ", '" 465 + UifConstants.BLOCKUI_REFRESHOPTS 466 + "');"; 467 } 468 469 this.setPreLoadScript(preLoadScript); 470 471 String onReadyScript = ""; 472 if (this.getOnDocumentReadyScript() != null) { 473 onReadyScript = this.getOnDocumentReadyScript(); 474 } 475 476 // initialize session timers for giving timeout warnings 477 if (sessionPolicy.isEnableTimeoutWarning()) { 478 // warning minutes gives us the time before the timeout occurs to give the warning, 479 // so we need to determine how long that should be from the session start 480 int sessionTimeoutInterval = ((UifFormBase) model).getSessionTimeoutInterval(); 481 int sessionWarningSeconds = sessionPolicy.getTimeoutWarningSeconds(); 482 483 if (sessionTimeoutInterval <= 0) { 484 // force to one hour when no setting detected 485 sessionTimeoutInterval = 3600; 486 } 487 488 if (sessionWarningSeconds >= sessionTimeoutInterval || sessionWarningSeconds <= 0) { 489 sessionWarningSeconds = sessionTimeoutInterval/10; 490 } 491 492 int sessionWarningInterval = sessionTimeoutInterval - sessionWarningSeconds; 493 494 onReadyScript = ScriptUtils.appendScript(onReadyScript, ScriptUtils.buildFunctionCall( 495 UifConstants.JsFunctions.INITIALIZE_SESSION_TIMERS, sessionWarningInterval, 496 sessionTimeoutInterval)); 497 } 498 499 onReadyScript = ScriptUtils.appendScript(onReadyScript, "jQuery.extend(jQuery.validator.messages, " 500 + ClientValidationUtils.generateValidatorMessagesOption() 501 + ");"); 502 503 this.setOnDocumentReadyScript(onReadyScript); 504 505 // Breadcrumb handling 506 breadcrumbOptions.finalizeBreadcrumbs(model, this, breadcrumbItem); 507 508 // Add validation default js options for validation framework to View's data attributes 509 Object groupValidationDataDefaults = KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean( 510 UifConstants.GROUP_VALIDATION_DEFAULTS_MAP_ID); 511 Object fieldValidationDataDefaults = KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean( 512 UifConstants.FIELD_VALIDATION_DEFAULTS_MAP_ID); 513 Object actionDataDefaults = KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean( 514 UifConstants.ACTION_DEFAULTS_MAP_ID); 515 Object requiredIndicator = KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean( 516 UifConstants.REQUIRED_INDICATOR_ID); 517 518 // Add data defaults for common components to the view for use in js (to reduce size of individual components) 519 this.addScriptDataAttribute(UifConstants.DataAttributes.GROUP_VALIDATION_DEFAULTS, ScriptUtils.convertToJsValue( 520 (Map<String, String>) groupValidationDataDefaults)); 521 this.addScriptDataAttribute(UifConstants.DataAttributes.FIELD_VALIDATION_DEFAULTS, ScriptUtils.convertToJsValue( 522 (Map<String, String>) fieldValidationDataDefaults)); 523 this.addScriptDataAttribute(UifConstants.DataAttributes.ACTION_DEFAULTS, ScriptUtils.convertToJsValue( 524 (Map<String, String>) actionDataDefaults)); 525 this.addScriptDataAttribute(UifConstants.DataAttributes.REQ_INDICATOR, (String) requiredIndicator); 526 527 // give view role attribute for js selections 528 this.addDataAttribute(UifConstants.DataAttributes.ROLE, UifConstants.RoleTypes.VIEW); 529 530 // Add state mapping to post metadata 531 ViewLifecycle.getViewPostMetadata().addComponentPostData(this, "stateObjectBindingPath", 532 stateObjectBindingPath); 533 ViewLifecycle.getViewPostMetadata().addComponentPostData(this, "stateMapping", stateMapping); 534 535 setNestedComponentId(getInstructionalMessage(), this.getId() + UifConstants.IdSuffixes.INSTRUCTIONAL); 536 537 Header header = getHeader(); 538 Help help = getHelp(); 539 540 if (header != null) { 541 setNestedComponentId(header, this.getId() + UifConstants.IdSuffixes.HEADER_WRAPPER); 542 setNestedComponentId(header.getUpperGroup(), this.getId() + UifConstants.IdSuffixes.HEADER_UPPER_GROUP); 543 setNestedComponentId(header.getRightGroup(), this.getId() + UifConstants.IdSuffixes.HEADER_RIGHT_GROUP); 544 setNestedComponentId(header.getLowerGroup(), this.getId() + UifConstants.IdSuffixes.HEADER_LOWER_GROUP); 545 setNestedComponentId(help, this.getId() + UifConstants.IdSuffixes.HELP_WRAPPER); 546 if (help != null && help.getHelpAction() != null) { 547 setNestedComponentId(help.getHelpAction(), this.getId() + UifConstants.IdSuffixes.HELP_WRAPPER); 548 } 549 } 550 } 551 552 /** 553 * Helper method for setting a new ID for the nested components 554 * 555 * @param component component to adjust ID for 556 * @param newId 557 */ 558 protected void setNestedComponentId(Component component, String newId) { 559 if (component != null) { 560 component.setId(newId); 561 } 562 } 563 564 /** 565 * {@inheritDoc} 566 */ 567 @Override 568 public void notifyCompleted(ViewLifecyclePhase phase) { 569 super.notifyCompleted(phase); 570 571 if (phase.getViewPhase().equals(UifConstants.ViewPhases.FINALIZE)) { 572 ViewLifecycle.getHelper().performCustomViewFinalize(ViewLifecycle.getModel()); 573 } 574 } 575 576 /** 577 * Gets all breadcrumb items related to this view's parent location. 578 * 579 * @return breadcrumb items 580 */ 581 public List<BreadcrumbItem> getBreadcrumbItems() { 582 if (parentLocation == null) { 583 return Collections.emptyList(); 584 } 585 586 List<BreadcrumbItem> breadcrumbItems = new ArrayList<BreadcrumbItem>(); 587 breadcrumbItems.add(parentLocation.getPageBreadcrumbItem()); 588 breadcrumbItems.add(parentLocation.getViewBreadcrumbItem()); 589 for (BreadcrumbItem item : parentLocation.getResolvedBreadcrumbItems()) { 590 if (!breadcrumbItems.contains(item)) { 591 breadcrumbItems.add(item); 592 } 593 } 594 595 return breadcrumbItems; 596 } 597 598 /** 599 * {@inheritDoc} 600 */ 601 @Override 602 public Set<Class<? extends Component>> getSupportedComponents() { 603 Set<Class<? extends Component>> supportedComponents = new HashSet<Class<? extends Component>>(); 604 supportedComponents.add(Group.class); 605 606 return supportedComponents; 607 } 608 609 /** 610 * {@inheritDoc} 611 */ 612 @Override 613 public String getComponentTypeName() { 614 return "view"; 615 } 616 617 /** 618 * Iterates through the contained page items and returns the Page that matches the set current page id or 619 * the first page in the case of a single page view. 620 * 621 * @return page group instance 622 */ 623 @ViewLifecycleRestriction(exclude = UifConstants.ViewPhases.PRE_PROCESS) 624 public PageGroup getCurrentPage() { 625 if (currentPage != null) { 626 return currentPage; 627 } 628 629 for (Component item : this.getItems()) { 630 if (!(item instanceof PageGroup)) { 631 continue; 632 } 633 634 if (singlePageView || StringUtils.equals(item.getId(), getCurrentPageId())) { 635 currentPage = (PageGroup) CopyUtils.unwrap(item); 636 } 637 } 638 639 return currentPage; 640 } 641 642 /** 643 * Getter for returning the view's items and page for inclusion in the pre-process phase. 644 * 645 * <p>Note this is necessary so we get IDs assigned for all the pages during the pre-process phase. For other 646 * phases, only the current page is picked up.</p> 647 * 648 * @return list of components to include for the pre-process phase 649 */ 650 @ViewLifecycleRestriction(UifConstants.ViewPhases.PRE_PROCESS) 651 public List<Component> getPagesForPreprocessing() { 652 List<Component> processProcessItems = new ArrayList<Component>(); 653 654 if (getItems() != null) { 655 processProcessItems.addAll(getItems()); 656 } 657 658 if (getPage() != null) { 659 processProcessItems.add(getPage()); 660 } 661 662 return processProcessItems; 663 } 664 665 /** 666 * {@inheritDoc} 667 */ 668 @Override 669 public void sortItems() { 670 if (!singlePageView) { 671 super.sortItems(); 672 } 673 } 674 675 /** 676 * Namespace code the view should be associated with. 677 * 678 * <p>The namespace code is used within the framework in such places as permission checks and parameter 679 * retrieval</p> 680 * 681 * @return namespace code 682 */ 683 @BeanTagAttribute 684 public String getNamespaceCode() { 685 return namespaceCode; 686 } 687 688 /** 689 * @see View#getNamespaceCode() 690 */ 691 public void setNamespaceCode(String namespaceCode) { 692 checkMutable(true); 693 this.namespaceCode = namespaceCode; 694 } 695 696 /** 697 * View name provides an identifier for a view within a type. That is if a 698 * set of View instances have the same values for the 699 * properties that are used to retrieve them by their type, the name can be 700 * given to further qualify the view that should be retrieved. 701 * 702 * <p>A view type like the LookupView might have several views for 703 * the same object class, but one that is the 'default' lookup and another 704 * that is the 'advanced' lookup. Therefore the name on the first could be 705 * set to 'default', and likewise the name for the second 'advanced'</p> 706 * 707 * @return name of view 708 */ 709 @BeanTagAttribute 710 public String getViewName() { 711 return this.viewName; 712 } 713 714 /** 715 * @see View#getViewName() 716 */ 717 public void setViewName(String viewName) { 718 checkMutable(true); 719 this.viewName = viewName; 720 } 721 722 /** 723 * When true, this view will use a unified header - the page header will be omitted and its title will be used 724 * in the ViewHeader supportTitle property (dynamically updated on page change). 725 * 726 * @return true if using a unified header 727 */ 728 @BeanTagAttribute 729 public boolean isUnifiedHeader() { 730 return unifiedHeader; 731 } 732 733 /** 734 * @see View#isUnifiedHeader() 735 */ 736 public void setUnifiedHeader(boolean unifiedHeader) { 737 checkMutable(true); 738 this.unifiedHeader = unifiedHeader; 739 } 740 741 /** 742 * TopGroup is an optional group of content that appears above the breadcrumbs and view header. 743 * 744 * @return the topGroup component 745 */ 746 @BeanTagAttribute 747 public Group getTopGroup() { 748 return topGroup; 749 } 750 751 /** 752 * @see View#getTopGroup() 753 */ 754 public void setTopGroup(Group topGroup) { 755 checkMutable(true); 756 this.topGroup = topGroup; 757 } 758 759 /** 760 * Header for the application containing the view. 761 * 762 * <p>When deploying outside a portal, the application header and footer property can be configured to 763 * display a consistent header/footer across all views. Here application logos, menus, login controls 764 * and so on can be rendered</p> 765 * 766 * @return application header 767 */ 768 @BeanTagAttribute 769 public Header getApplicationHeader() { 770 return applicationHeader; 771 } 772 773 /** 774 * @see View#getApplicationHeader() 775 */ 776 public void setApplicationHeader(Header applicationHeader) { 777 checkMutable(true); 778 this.applicationHeader = applicationHeader; 779 } 780 781 /** 782 * Footer for the application containing the view. 783 * 784 * <p>When deploying outside a portal, the application header and footer property can be configured to 785 * display a consistent header/footer across all views. Here such things as application links, copyrights 786 * and so on can be rendered</p> 787 * 788 * @return application footer 789 */ 790 @BeanTagAttribute 791 public Group getApplicationFooter() { 792 return applicationFooter; 793 } 794 795 /** 796 * @see View#getApplicationFooter() 797 */ 798 public void setApplicationFooter(Group applicationFooter) { 799 checkMutable(true); 800 this.applicationFooter = applicationFooter; 801 } 802 803 /** 804 * Build footer for the application containing the view. 805 * 806 * @return build footer 807 */ 808 @BeanTagAttribute 809 public Group getBuildFooter() { 810 return buildFooter; 811 } 812 813 /** 814 * @see View#getBuildFooter() 815 */ 816 public void setBuildFooter(Group buildFooter) { 817 this.buildFooter = buildFooter; 818 } 819 820 /** 821 * Title text to be displayed on browser tab. 822 * 823 * @return 824 */ 825 @BeanTagAttribute(name = "applicationTitleText") 826 public String getApplicationTitleText() { 827 return applicationTitleText; 828 } 829 830 /** 831 * @see View#getApplicationTitleText() 832 */ 833 public void setApplicationTitleText(String applicationTitleText) { 834 this.applicationTitleText = applicationTitleText; 835 } 836 837 /** 838 * If true, the top group will be sticky (fixed to top of window). 839 * 840 * @return true if the top group is sticky, false otherwise 841 */ 842 @BeanTagAttribute 843 public boolean isStickyTopGroup() { 844 return stickyTopGroup; 845 } 846 847 /** 848 * @see View#isStickyTopGroup() 849 */ 850 public void setStickyTopGroup(boolean stickyTopGroup) { 851 checkMutable(true); 852 this.stickyTopGroup = stickyTopGroup; 853 } 854 855 /** 856 * If true, the breadcrumb widget will be sticky (fixed to top of window). 857 * 858 * @return true if breadcrumbs are sticky, false otherwise 859 */ 860 @BeanTagAttribute 861 public boolean isStickyBreadcrumbs() { 862 return stickyBreadcrumbs; 863 } 864 865 /** 866 * @see View#isStickyBreadcrumbs() 867 */ 868 public void setStickyBreadcrumbs(boolean stickyBreadcrumbs) { 869 checkMutable(true); 870 this.stickyBreadcrumbs = stickyBreadcrumbs; 871 } 872 873 /** 874 * If true, the ViewHeader for this view will be sticky (fixed to top of window). 875 * 876 * @return true if the header is sticky, false otherwise 877 */ 878 @BeanTagAttribute 879 public boolean isStickyHeader() { 880 if (this.getHeader() != null && this.getHeader() instanceof ViewHeader) { 881 return ((ViewHeader) this.getHeader()).isSticky(); 882 } else { 883 return false; 884 } 885 } 886 887 /** 888 * @see View#isStickyHeader() 889 */ 890 public void setStickyHeader(boolean stickyHeader) { 891 checkMutable(true); 892 this.stickyHeader = stickyHeader; 893 if (this.getHeader() != null && this.getHeader() instanceof ViewHeader) { 894 ((ViewHeader) this.getHeader()).setSticky(stickyHeader); 895 } 896 } 897 898 /** 899 * Set to true to make the applicationHeader sticky (fixed to top of window) 900 * 901 * @return true if applicationHeader is sticky, false otherwise 902 */ 903 @BeanTagAttribute 904 public boolean isStickyApplicationHeader() { 905 return stickyApplicationHeader; 906 } 907 908 /** 909 * @see View#isStickyApplicationHeader() 910 */ 911 public void setStickyApplicationHeader(boolean stickyApplicationHeader) { 912 checkMutable(true); 913 this.stickyApplicationHeader = stickyApplicationHeader; 914 } 915 916 /** 917 * If true, the view footer will become sticky (fixed to bottom of window). 918 * 919 * @return ture if the view footer is sticky, false otherwise 920 */ 921 @BeanTagAttribute 922 public boolean isStickyFooter() { 923 return stickyFooter; 924 } 925 926 /** 927 * @see View#isStickyFooter() 928 */ 929 public void setStickyFooter(boolean stickyFooter) { 930 checkMutable(true); 931 this.stickyFooter = stickyFooter; 932 if (this.getFooter() != null) { 933 this.getFooter().addDataAttribute(UifConstants.DataAttributes.STICKY_FOOTER, Boolean.toString( 934 stickyFooter)); 935 } 936 } 937 938 /** 939 * If true, the applicationFooter will become sticky (fixed to bottom of window). 940 * 941 * @return true if the application footer is sticky, false otherwise 942 */ 943 @BeanTagAttribute 944 public boolean isStickyApplicationFooter() { 945 return stickyApplicationFooter; 946 } 947 948 /** 949 * @see View#isStickyApplicationFooter() 950 */ 951 public void setStickyApplicationFooter(boolean stickyApplicationFooter) { 952 checkMutable(true); 953 this.stickyApplicationFooter = stickyApplicationFooter; 954 } 955 956 /** 957 * List of CSS style classes that will be applied to a div that wraps the content. 958 * 959 * <p>Wrapping the content gives the ability to move between a fluid width container or a fixed width 960 * container. The div is also wraps content inside sticky elements (header and footer), so visual treatment 961 * can be given to the full width of the screen while restricting the area of the content.</p> 962 * 963 * <p>In Bootstrap, use 'container-fluid' for a fluid width container, and 'container' for a fixed width 964 * container.</p> 965 * 966 * @return List of css classes to apply to content wrapper div 967 */ 968 @BeanTagAttribute 969 public List<String> getContentContainerCssClasses() { 970 return contentContainerCssClasses; 971 } 972 973 /** 974 * @see View#getContentContainerCssClasses() 975 */ 976 public void setContentContainerCssClasses(List<String> contentContainerCssClasses) { 977 this.contentContainerCssClasses = contentContainerCssClasses; 978 } 979 980 /** 981 * Returns the list of {@link View#getContentContainerCssClasses()} as a concatenated string (each class 982 * is separated by a space). 983 * 984 * @return String of content css classes 985 */ 986 public String getContentContainerClassesAsString() { 987 if (contentContainerCssClasses != null) { 988 return StringUtils.join(contentContainerCssClasses, " "); 989 } 990 991 return ""; 992 } 993 994 /** 995 * Specifies what page should be rendered by default. This is the page that 996 * will be rendered when the View is first rendered or when the 997 * current page is not set 998 * 999 * @return id of the page to render by default 1000 */ 1001 @BeanTagAttribute 1002 public String getEntryPageId() { 1003 return this.entryPageId; 1004 } 1005 1006 /** 1007 * @see View#getEntryPageId() 1008 */ 1009 public void setEntryPageId(String entryPageId) { 1010 checkMutable(true); 1011 this.entryPageId = entryPageId; 1012 } 1013 1014 /** 1015 * The id for the page within the view that should be displayed in the UI. Other pages of the view will not be 1016 * rendered. 1017 * 1018 * <p>If current page id is not set, it is set to the configured entry page or first item in list id</p> 1019 * 1020 * @return id of the page that should be displayed 1021 */ 1022 public String getCurrentPageId() { 1023 // default current page if not set 1024 if (StringUtils.isBlank(currentPageId)) { 1025 if (StringUtils.isNotBlank(entryPageId)) { 1026 currentPageId = entryPageId; 1027 } else if ((getItems() != null) && !getItems().isEmpty()) { 1028 Component firstPageGroup = getItems().get(0); 1029 if (firstPageGroup instanceof PageGroup) { 1030 currentPageId = firstPageGroup.getId(); 1031 } 1032 } 1033 } 1034 1035 return this.currentPageId; 1036 } 1037 1038 /** 1039 * @see View#getCurrentPageId() 1040 */ 1041 public void setCurrentPageId(String currentPageId) { 1042 checkMutable(true); 1043 this.currentPageId = currentPageId; 1044 this.currentPage = null; 1045 } 1046 1047 /** 1048 * NavigationGroup instance for the View< 1049 * 1050 * <p>Provides configuration necessary to render the navigation. This includes 1051 * navigation items in addition to configuration for the navigation 1052 * renderer</p> 1053 * 1054 * @return NavigationGroup 1055 */ 1056 @BeanTagAttribute 1057 public Group getNavigation() { 1058 return this.navigation; 1059 } 1060 1061 /** 1062 * @see View#getNavigation() 1063 */ 1064 public void setNavigation(Group navigation) { 1065 checkMutable(true); 1066 this.navigation = navigation; 1067 } 1068 1069 /** 1070 * Class of the Form that should be used with the View instance. 1071 * 1072 * <p>The form is the top level object for all the view's data and is 1073 * used to present and accept data in the user interface. All form classes 1074 * should extend UifFormBase</p> 1075 * 1076 * @return class for the view's form 1077 * @see org.kuali.rice.krad.web.form.UifFormBase 1078 */ 1079 @BeanTagAttribute 1080 public Class<?> getFormClass() { 1081 return this.formClass; 1082 } 1083 1084 /** 1085 * @see View#getFormClass() 1086 */ 1087 public void setFormClass(Class<?> formClass) { 1088 checkMutable(true); 1089 this.formClass = formClass; 1090 } 1091 1092 /** 1093 * For View types that work primarily with one nested object of 1094 * the form (for instance document, or bo) the default binding object path 1095 * can be set for each of the views DataBinding components. If 1096 * the component does not set its own binding object path it will inherit 1097 * the default. 1098 * 1099 * @return binding path to the object from the form 1100 */ 1101 @BeanTagAttribute 1102 public String getDefaultBindingObjectPath() { 1103 return this.defaultBindingObjectPath; 1104 } 1105 1106 /** 1107 * @see View#getDefaultBindingObjectPath() 1108 */ 1109 public void setDefaultBindingObjectPath(String defaultBindingObjectPath) { 1110 checkMutable(true); 1111 this.defaultBindingObjectPath = defaultBindingObjectPath; 1112 } 1113 1114 /** 1115 * Configures the concrete classes that will be used for properties in the 1116 * form object graph that have an abstract or interface type. 1117 * 1118 * <p>For properties that have an abstract or interface type, it is not 1119 * possible to perform operations like getting/settings property values and 1120 * getting information from the dictionary. When these properties are 1121 * encountered in the object graph, this Map will be consulted 1122 * to determine the concrete type to use</p> 1123 * 1124 * <p>e.g. Suppose we have a property document.accountingLine.accountNumber and 1125 * the accountingLine property on the document instance has an interface 1126 * type 'AccountingLine'. We can then put an entry into this map with key 1127 * 'document.accountingLine', and value 1128 * 'org.kuali.rice.sampleapp.TravelAccountingLine'. When getting the 1129 * property type or an entry from the dictionary for accountNumber, the 1130 * TravelAccountingLine class will be used</p> 1131 * 1132 * @return Map<String, Class> of class implementations keyed by path 1133 */ 1134 @BeanTagAttribute 1135 public Map<String, Class<?>> getObjectPathToConcreteClassMapping() { 1136 if (objectPathToConcreteClassMapping == Collections.EMPTY_MAP && isMutable(true)) { 1137 objectPathToConcreteClassMapping = new HashMap<String, Class<?>>(); 1138 } 1139 1140 return this.objectPathToConcreteClassMapping; 1141 } 1142 1143 /** 1144 * @see View#getObjectPathToConcreteClassMapping() 1145 */ 1146 public void setObjectPathToConcreteClassMapping(Map<String, Class<?>> objectPathToConcreteClassMapping) { 1147 checkMutable(true); 1148 this.objectPathToConcreteClassMapping = objectPathToConcreteClassMapping; 1149 } 1150 1151 /** 1152 * Declares additional script files that should be included with the 1153 * View. 1154 * 1155 * <p>These files are brought into the HTML page along with 1156 * common script files configured for the Rice application. Each entry 1157 * contain the path to the CSS file, either a relative path, path from web 1158 * root, or full URI</p> 1159 * 1160 * <p>e.g. '/krad/scripts/myScript.js', '../scripts/myScript.js', 1161 * 'http://my.edu/web/myScript.js'</p> 1162 * 1163 * @return script file locations 1164 */ 1165 @BeanTagAttribute 1166 public List<String> getAdditionalScriptFiles() { 1167 if (additionalScriptFiles == Collections.EMPTY_LIST && isMutable(true)) { 1168 additionalScriptFiles = new LifecycleAwareList<String>(this); 1169 } 1170 1171 return additionalScriptFiles; 1172 } 1173 1174 /** 1175 * @see View#getAdditionalScriptFiles() 1176 */ 1177 public void setAdditionalScriptFiles(List<String> additionalScriptFiles) { 1178 checkMutable(true); 1179 if (additionalScriptFiles == null) { 1180 this.additionalScriptFiles = Collections.emptyList(); 1181 } else { 1182 this.additionalScriptFiles = new LifecycleAwareList<String>(this, additionalScriptFiles); 1183 } 1184 } 1185 1186 /** 1187 * Declares additional CSS files that should be included with the View. 1188 * 1189 * <p>These files are brought into the HTML page along with 1190 * common CSS files configured for the Rice application. Each entry should 1191 * contain the path to the CSS file, either a relative path, path from web 1192 * root, or full URI</p> 1193 * 1194 * <p>e.g. '/krad/css/stacked-view.css', '../css/stacked-view.css', 1195 * 'http://my.edu/web/stacked-view.css'</p> 1196 * 1197 * @return CSS file locations 1198 */ 1199 @BeanTagAttribute 1200 public List<String> getAdditionalCssFiles() { 1201 if (additionalCssFiles == Collections.EMPTY_LIST && isMutable(true)) { 1202 additionalCssFiles = new LifecycleAwareList<String>(this); 1203 } 1204 1205 return additionalCssFiles; 1206 } 1207 1208 /** 1209 * @see View#getAdditionalCssFiles() 1210 */ 1211 public void setAdditionalCssFiles(List<String> additionalCssFiles) { 1212 checkMutable(true); 1213 if (additionalCssFiles == null) { 1214 this.additionalCssFiles = Collections.emptyList(); 1215 } else { 1216 this.additionalCssFiles = new LifecycleAwareList<String>(this, additionalCssFiles); 1217 } 1218 } 1219 1220 /** 1221 * List of additional link tags that should be included with the View in the html head. 1222 * 1223 * @return headlink objects 1224 */ 1225 @BeanTagAttribute 1226 public List<HeadLink> getAdditionalHeadLinks() { 1227 return additionalHeadLinks; 1228 } 1229 1230 /** 1231 * @see View#getAdditionalHeadLinks() 1232 */ 1233 public void setAdditionalHeadLinks(List<HeadLink> additionalHeadLinks) { 1234 this.additionalHeadLinks = additionalHeadLinks; 1235 } 1236 1237 /** 1238 * List of additional meta tags that should be included with the View in the html head tag. 1239 * 1240 * @return additionalMetaTags 1241 */ 1242 @BeanTagAttribute 1243 public List<MetaTag> getAdditionalMetaTags() { 1244 return additionalMetaTags; 1245 } 1246 1247 /** 1248 * @see View#getAdditionalMetaTags() 1249 */ 1250 public void setAdditionalMetaTags(List<MetaTag> additionalMetaTags) { 1251 this.additionalMetaTags = additionalMetaTags; 1252 } 1253 1254 /** 1255 * True if the libraryCssClasses set on components will be output to their class attribute, false otherwise. 1256 * 1257 * @return true if using libraryCssClasses on components 1258 */ 1259 @BeanTagAttribute 1260 public boolean isUseLibraryCssClasses() { 1261 return useLibraryCssClasses; 1262 } 1263 1264 /** 1265 * @see View#isUseLibraryCssClasses() 1266 */ 1267 public void setUseLibraryCssClasses(boolean useLibraryCssClasses) { 1268 checkMutable(true); 1269 this.useLibraryCssClasses = useLibraryCssClasses; 1270 } 1271 1272 /** 1273 * List of templates that are used to render the view. 1274 * 1275 * <p>This list will be populated by unique template names as the components of the view are being processed. 1276 * Additional templates can be added in the view configuration if desired. At the beginning of the the view 1277 * rendering, each template in the list will then be included or processed by the template language</p> 1278 * 1279 * <p>Note the user of this depends on the template language being used for rendering. Some languages might require 1280 * including the template for each component instance (for example JSP templates). While others might simply 1281 * include markup that is then available for rendering each component instance (for example FreeMarker which has 1282 * a macro for each component that the template defines)</p> 1283 * 1284 * @return list of template names that should be included for rendering the view 1285 */ 1286 public List<String> getViewTemplates() { 1287 return viewTemplates; 1288 } 1289 1290 /** 1291 * Adds a template to the views include list. 1292 * 1293 * @param template path to template to add 1294 */ 1295 public void addViewTemplate(String template) { 1296 if (StringUtils.isEmpty(template)) { 1297 return; 1298 } 1299 1300 if (!viewTemplates.contains(template)) { 1301 synchronized (viewTemplates) { 1302 viewTemplates.add(template); 1303 } 1304 } 1305 } 1306 1307 /** 1308 * Setter for the the list of template names that should be included to render the view 1309 * 1310 * @param viewTemplates 1311 */ 1312 public void setViewTemplates(List<String> viewTemplates) { 1313 checkMutable(true); 1314 1315 if (viewTemplates == null) { 1316 this.viewTemplates = new LifecycleAwareList<String>(this); 1317 } else { 1318 this.viewTemplates = new LifecycleAwareList<String>(this, viewTemplates); 1319 } 1320 } 1321 1322 /** 1323 * View type name the view is associated with the view instance 1324 * 1325 * <p>Views that share common features and functionality can be grouped by the 1326 * view type. Usually view types extend the <code>View</code> class to 1327 * provide additional configuration and to set defaults. View types can also 1328 * implement the <code>ViewTypeService</code> to add special indexing and 1329 * retrieval of views</p> 1330 * 1331 * @return view type name for the view 1332 */ 1333 @BeanTagAttribute 1334 public ViewType getViewTypeName() { 1335 return this.viewTypeName; 1336 } 1337 1338 /** 1339 * @see View#getViewTypeName() 1340 */ 1341 public void setViewTypeName(ViewType viewTypeName) { 1342 checkMutable(true); 1343 this.viewTypeName = viewTypeName; 1344 } 1345 1346 /** 1347 * Class name of the ViewHelperService that handles the various phases of the Views lifecycle. 1348 * 1349 * @return Class for the spring bean 1350 * @see org.kuali.rice.krad.uif.service.ViewHelperService 1351 */ 1352 @BeanTagAttribute 1353 public Class<? extends ViewHelperService> getViewHelperServiceClass() { 1354 return this.viewHelperServiceClass; 1355 } 1356 1357 /** 1358 * Setter for the <code>ViewHelperService</code> class name 1359 * Also initializes the viewHelperService 1360 * 1361 * @param viewHelperServiceClass 1362 */ 1363 public void setViewHelperServiceClass(Class<? extends ViewHelperService> viewHelperServiceClass) { 1364 checkMutable(true); 1365 this.viewHelperServiceClass = viewHelperServiceClass; 1366 if ((this.viewHelperService == null) && (this.viewHelperServiceClass != null)) { 1367 viewHelperService = KRADUtils.createNewObjectFromClass(viewHelperServiceClass); 1368 } 1369 } 1370 1371 /** 1372 * Creates the <code>ViewHelperService</code> associated with the View 1373 * 1374 * @return ViewHelperService instance 1375 */ 1376 @BeanTagAttribute 1377 public ViewHelperService getViewHelperService() { 1378 return viewHelperService; 1379 } 1380 1381 /** 1382 * @see View#getViewHelperServiceClass() 1383 */ 1384 public void setViewHelperService(ViewHelperService viewHelperService) { 1385 checkMutable(true); 1386 this.viewHelperService = viewHelperService; 1387 } 1388 1389 /** 1390 * Invoked to produce a ViewIndex of the current view's components 1391 */ 1392 public void clearIndex() { 1393 if (this.viewIndex == null) { 1394 this.viewIndex = new ViewIndex(); 1395 } 1396 this.viewIndex.clearIndex(this); 1397 } 1398 1399 /** 1400 * Holds field indexes of the View instance for retrieval. 1401 * 1402 * @return ViewIndex instance 1403 */ 1404 public ViewIndex getViewIndex() { 1405 return this.viewIndex; 1406 } 1407 1408 /** 1409 * Map of parameters from the request that set view options, used to rebuild 1410 * the view on each post 1411 * 1412 * <p> 1413 * Views can be configured by parameters. These might impact which parts of 1414 * the view are rendered or how the view behaves. Generally these would get 1415 * passed in when a new view is requested (by request parameters). These 1416 * will be used to initially populate the view properties. In addition, on a 1417 * post the view will be rebuilt and properties reset again by the allow 1418 * request parameters. 1419 * </p> 1420 * 1421 * <p> 1422 * Example parameter would be for MaintenaceView whether a New, Edit, or 1423 * Copy was requested (maintenance mode) 1424 * </p> 1425 * 1426 * @return 1427 */ 1428 public Map<String, String> getViewRequestParameters() { 1429 return this.viewRequestParameters; 1430 } 1431 1432 /** 1433 * @see View#getViewRequestParameters() 1434 */ 1435 public void setViewRequestParameters(Map<String, String> viewRequestParameters) { 1436 checkMutable(true); 1437 this.viewRequestParameters = Collections.unmodifiableMap(viewRequestParameters); 1438 } 1439 1440 /** 1441 * Indicates whether the form (model) associated with the view should be stored in the user session. 1442 * 1443 * <p>The form class (or model) is used to hold the data that backs the view along with the built view object. Storing 1444 * the form instance in session allows many things: 1445 * 1446 * <ul> 1447 * <li>Data does not need to be rebuilt for each server request (for example a collection)</li> 1448 * <li>Data that does not need to go to the user can remain on the form, reducing the size of the response and 1449 * improving security</li> 1450 * <li>Data can be keep around in a 'pre-save' state. When requested by the user changes can then be persisted to 1451 * the database</li> 1452 * <li>Certain information about the view that was rendered, such as input fields, collection paths, and refresh 1453 * components can be kept on the form to support UI interaction</li> 1454 * </ul> 1455 * 1456 * Setting this flag to false will prevent the form from being kept in session and as a result will limit what can 1457 * be done by the framework. In almost all cases this is not recommended</p> 1458 * 1459 * <p>Note all forms will be cleared when the user session expires (based on the rice configuration). In addition, the 1460 * framework enables clear points on certain actions to remove the form when it is no longer needed</p> 1461 * 1462 * @return true if the form should be stored in the user session, false if only request based 1463 */ 1464 @BeanTagAttribute 1465 public boolean isPersistFormToSession() { 1466 return persistFormToSession; 1467 } 1468 1469 /** 1470 * @see View#isPersistFormToSession() 1471 */ 1472 public void setPersistFormToSession(boolean persistFormToSession) { 1473 checkMutable(true); 1474 this.persistFormToSession = persistFormToSession; 1475 } 1476 1477 /** 1478 * Configures behavior that should occur when a session timeout occurs on the view. 1479 * 1480 * @return view session policy instance 1481 */ 1482 @BeanTagAttribute 1483 public ViewSessionPolicy getSessionPolicy() { 1484 return sessionPolicy; 1485 } 1486 1487 /** 1488 * @see View#getSessionPolicy() 1489 */ 1490 public void setSessionPolicy(ViewSessionPolicy sessionPolicy) { 1491 checkMutable(true); 1492 this.sessionPolicy = sessionPolicy; 1493 } 1494 1495 public int getPreloadPoolSize() { 1496 return preloadPoolSize; 1497 } 1498 1499 public void setPreloadPoolSize(int preloadPoolSize) { 1500 this.preloadPoolSize = preloadPoolSize; 1501 } 1502 1503 /** 1504 * PresentationController that should be used for the View instance. 1505 * 1506 * <p>The presentation controller is consulted to determine component (group, 1507 * field) state such as required, read-only, and hidden. The presentation 1508 * controller does not take into account user permissions. The presentation 1509 * controller can also output action flags and edit modes that will be set 1510 * onto the view instance and can be referred to by conditional expressions</p> 1511 * 1512 * @return PresentationController 1513 */ 1514 @BeanTagAttribute 1515 public ViewPresentationController getPresentationController() { 1516 return this.presentationController; 1517 } 1518 1519 /** 1520 * @see View#getPresentationController() 1521 */ 1522 public void setPresentationController(ViewPresentationController presentationController) { 1523 checkMutable(true); 1524 this.presentationController = presentationController; 1525 } 1526 1527 /** 1528 * Setter for the view's presentation controller by class 1529 * 1530 * @param presentationControllerClass 1531 */ 1532 public void setPresentationControllerClass( 1533 Class<? extends ViewPresentationController> presentationControllerClass) { 1534 checkMutable(true); 1535 this.presentationController = KRADUtils.createNewObjectFromClass(presentationControllerClass); 1536 } 1537 1538 /** 1539 * Authorizer that should be used for the View instance 1540 * 1541 * <p>The authorizer class is consulted to determine component (group, field) 1542 * state such as required, read-only, and hidden based on the users 1543 * permissions. It typically communicates with the Kuali Identity Management 1544 * system to determine roles and permissions. It is used with the 1545 * presentation controller and dictionary conditional logic to determine the 1546 * final component state. The authorizer can also output action flags and 1547 * edit modes that will be set onto the view instance and can be referred to 1548 * by conditional expressions</p> 1549 * 1550 * @return Authorizer 1551 */ 1552 @BeanTagAttribute 1553 public ViewAuthorizer getAuthorizer() { 1554 return this.authorizer; 1555 } 1556 1557 /** 1558 * @see View#getAuthorizer() 1559 */ 1560 public void setAuthorizer(ViewAuthorizer authorizer) { 1561 checkMutable(true); 1562 this.authorizer = authorizer; 1563 } 1564 1565 /** 1566 * Setter for the view's authorizer by class 1567 * 1568 * @param authorizerClass 1569 */ 1570 public void setAuthorizerClass(Class<? extends ViewAuthorizer> authorizerClass) { 1571 checkMutable(true); 1572 this.authorizer = KRADUtils.createNewObjectFromClass(authorizerClass); 1573 } 1574 1575 /** 1576 * Map of strings that flag what actions can be taken in the UI. 1577 * 1578 * <p>These can be used in conditional expressions in the dictionary or by 1579 * other UI logic</p> 1580 * 1581 * @return action flags 1582 */ 1583 @BeanTagAttribute 1584 public Map<String, Boolean> getActionFlags() { 1585 return this.actionFlags; 1586 } 1587 1588 /** 1589 * @see View#getActionFlags() 1590 */ 1591 public void setActionFlags(Map<String, Boolean> actionFlags) { 1592 this.actionFlags = actionFlags; 1593 } 1594 1595 /** 1596 * Map of edit modes that enabled for the view. 1597 * 1598 * <p>These can be used in conditional expressions in the dictionary or by 1599 * other UI logic</p> 1600 * 1601 * @return edit modes 1602 */ 1603 @BeanTagAttribute 1604 public Map<String, Boolean> getEditModes() { 1605 return this.editModes; 1606 } 1607 1608 /** 1609 * @see View#getEditModes() 1610 */ 1611 public void setEditModes(Map<String, Boolean> editModes) { 1612 this.editModes = editModes; 1613 } 1614 1615 public Class<? extends RequestAuthorizationCache> getRequestAuthorizationCacheClass() { 1616 return requestAuthorizationCacheClass; 1617 } 1618 1619 public void setRequestAuthorizationCacheClass( 1620 Class<? extends RequestAuthorizationCache> requestAuthorizationCacheClass) { 1621 this.requestAuthorizationCacheClass = requestAuthorizationCacheClass; 1622 } 1623 1624 /** 1625 * Map that contains expressions to evaluate and make available as variables 1626 * for conditional expressions within the view. 1627 * 1628 * <p>Each Map entry contains one expression variables, where the map key gives 1629 * the name for the variable, and the map value gives the variable 1630 * expression. The variables expressions will be evaluated before 1631 * conditional logic is run and made available as variables for other 1632 * conditional expressions. Variable expressions can be based on the model 1633 * and any object contained in the view's context</p> 1634 * 1635 * @return variable expressions 1636 */ 1637 @BeanTagAttribute 1638 public Map<String, String> getExpressionVariables() { 1639 return this.expressionVariables; 1640 } 1641 1642 /** 1643 * @see View#getExpressionVariables() 1644 */ 1645 public void setExpressionVariables(Map<String, String> expressionVariables) { 1646 checkMutable(true); 1647 this.expressionVariables = Collections.unmodifiableMap(expressionVariables); 1648 } 1649 1650 /** 1651 * Indicates whether the View only has a single page 1652 * Group or contains multiple page Group instances. 1653 * 1654 * <p>In the case of a single page it is assumed the group's items 1655 * list contains the section groups for the page, and the page itself is 1656 * given by the page property ({@link #getPage()}. This is for convenience 1657 * of configuration and also can drive other configuration like styling</p> 1658 * 1659 * @return true if the view only contains one page group, false if 1660 * it contains multple pages 1661 */ 1662 @BeanTagAttribute 1663 public boolean isSinglePageView() { 1664 return this.singlePageView; 1665 } 1666 1667 /** 1668 * @see View#isSinglePageView() 1669 */ 1670 public void setSinglePageView(boolean singlePageView) { 1671 checkMutable(true); 1672 this.singlePageView = singlePageView; 1673 } 1674 1675 /** 1676 * Indicates whether the default sections specified in the page items list 1677 * should be included for this view. This only applies to single paged views. 1678 * 1679 * @return true if the view should contain the default sections 1680 * specified in the page 1681 */ 1682 @BeanTagAttribute 1683 public boolean isMergeWithPageItems() { 1684 return mergeWithPageItems; 1685 } 1686 1687 /** 1688 * @see View#isMergeWithPageItems() 1689 */ 1690 public void setMergeWithPageItems(boolean mergeWithPageItems) { 1691 checkMutable(true); 1692 this.mergeWithPageItems = mergeWithPageItems; 1693 } 1694 1695 /** 1696 * For single paged views ({@link #isSinglePageView()}, gives the page 1697 * Group the view should render. The actual items for the page 1698 * is taken from the group's items list ({@link #getItems()}, and set onto 1699 * the give page group. This is for convenience of configuration. 1700 * 1701 * @return page group for single page views 1702 */ 1703 @ViewLifecycleRestriction 1704 @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECT) 1705 public PageGroup getPage() { 1706 return this.page; 1707 } 1708 1709 /** 1710 * @see View#getPage() 1711 */ 1712 public void setPage(PageGroup page) { 1713 checkMutable(true); 1714 this.page = page; 1715 } 1716 1717 /** 1718 * {@inheritDoc} 1719 */ 1720 @Override 1721 @ViewLifecycleRestriction 1722 @BeanTagAttribute 1723 public List<? extends Component> getItems() { 1724 if (items == Collections.EMPTY_LIST && isMutable(true)) { 1725 items = new LifecycleAwareList<Component>(this); 1726 } 1727 1728 return items; 1729 } 1730 1731 /** 1732 * {@inheritDoc} 1733 */ 1734 @SuppressWarnings("unchecked") 1735 @Override 1736 public void setItems(List<? extends Component> items) { 1737 checkMutable(true); 1738 1739 if (items == null) { 1740 this.items = Collections.emptyList(); 1741 } else { 1742 // TODO: Fix this unchecked condition. 1743 this.items = new LifecycleAwareList<Component>(this, (List<Component>) items); 1744 } 1745 } 1746 1747 /** 1748 * Provide a list of dialog groups associated with this view. 1749 * 1750 * @return List of dialog Groups 1751 */ 1752 @BeanTagAttribute 1753 @ViewLifecycleRestriction(value = UifConstants.ViewPhases.FINALIZE, 1754 condition = "!ajaxRequest || (ajaxReturnType eq 'update-view') || (ajaxReturnType eq 'update-page')") 1755 public List<Group> getDialogs() { 1756 if (dialogs == Collections.EMPTY_LIST && isMutable(true)) { 1757 dialogs = new LifecycleAwareList<Group>(this); 1758 } 1759 1760 return dialogs; 1761 } 1762 1763 /** 1764 * @see View#getDialogs() 1765 */ 1766 public void setDialogs(List<Group> dialogs) { 1767 checkMutable(true); 1768 1769 if (dialogs == null) { 1770 this.dialogs = Collections.emptyList(); 1771 } else { 1772 this.dialogs = new LifecycleAwareList<Group>(this, dialogs); 1773 } 1774 } 1775 1776 /** 1777 * Breadcrumb widget used for displaying homeward path and history 1778 * 1779 * @return the breadcrumbs 1780 */ 1781 @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE) 1782 public Breadcrumbs getBreadcrumbs() { 1783 return this.breadcrumbs; 1784 } 1785 1786 /** 1787 * @param breadcrumbs the breadcrumbs to set 1788 */ 1789 public void setBreadcrumbs(Breadcrumbs breadcrumbs) { 1790 checkMutable(true); 1791 this.breadcrumbs = breadcrumbs; 1792 } 1793 1794 /** 1795 * The breadcrumbOptions for this view. 1796 * 1797 * <p>Render options set at the view level are always ignored (only apply to 1798 * page level BreadcrumbOptions). BreadcrumbOptions for homewardPathBreadcrumbs, 1799 * preViewBreadcrumbs, prePageBreadcrumbs, 1800 * and breadcrumbOverrides are inherited by 1801 * child pages unless they override them themselves.</p> 1802 * 1803 * @return the BreadcrumbOptions for this view 1804 */ 1805 @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE) 1806 public BreadcrumbOptions getBreadcrumbOptions() { 1807 return breadcrumbOptions; 1808 } 1809 1810 /** 1811 * @see View#getBreadcrumbOptions() 1812 */ 1813 public void setBreadcrumbOptions(BreadcrumbOptions breadcrumbOptions) { 1814 checkMutable(true); 1815 this.breadcrumbOptions = breadcrumbOptions; 1816 } 1817 1818 /** 1819 * The View's breadcrumbItem defines settings for the breadcrumb which appears in the breadcrumb list for this 1820 * view. 1821 * 1822 * @return the breadcrumbItem 1823 */ 1824 @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE) 1825 public BreadcrumbItem getBreadcrumbItem() { 1826 return breadcrumbItem; 1827 } 1828 1829 /** 1830 * @see View#getBreadcrumbItem() 1831 */ 1832 public void setBreadcrumbItem(BreadcrumbItem breadcrumbItem) { 1833 checkMutable(true); 1834 this.breadcrumbItem = breadcrumbItem; 1835 } 1836 1837 /** 1838 * The parentLocation defines urls that represent the parent of a View in a conceptial site hierarchy. 1839 * 1840 * <p>By defining a parent with these urls defined, a breadcrumb chain can be generated and displayed automatically 1841 * before this View's breadcrumbItem(s). To chain multiple views, the urls must be defining viewId and 1842 * controllerMapping settings instead of setting an href directly (this will end the chain). If labels are 1843 * not set on parentLocations, the labels will attempt to be derived from parent views/pages breadcrumbItem 1844 * and headerText - if these contain expressions which cannot be evaluated in the current context an exception 1845 * will be thrown</p> 1846 * 1847 * @return the parentLocation 1848 */ 1849 @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE) 1850 public ParentLocation getParentLocation() { 1851 return parentLocation; 1852 } 1853 1854 /** 1855 * @see View#getParentLocation() 1856 */ 1857 public void setParentLocation(ParentLocation parentLocation) { 1858 checkMutable(true); 1859 this.parentLocation = parentLocation; 1860 } 1861 1862 /** 1863 * The pathBasedBreadcrumbs for this View. These can only be set by the framework. 1864 * 1865 * @return the path based breadcrumbs 1866 */ 1867 public List<BreadcrumbItem> getPathBasedBreadcrumbs() { 1868 return pathBasedBreadcrumbs; 1869 } 1870 1871 /** 1872 * The pathBasedBreadcrumbs for this View. 1873 * 1874 * @param pathBasedBreadcrumbs 1875 */ 1876 public void setPathBasedBreadcrumbs(List<BreadcrumbItem> pathBasedBreadcrumbs) { 1877 checkMutable(true); 1878 this.pathBasedBreadcrumbs = pathBasedBreadcrumbs == null ? null : new LifecycleAwareList<BreadcrumbItem>(this, 1879 pathBasedBreadcrumbs); 1880 } 1881 1882 /** 1883 * Growls widget which sets up global settings for the growls used in this 1884 * view and its pages. 1885 * 1886 * @return the growls 1887 */ 1888 @BeanTagAttribute 1889 public Growls getGrowls() { 1890 return this.growls; 1891 } 1892 1893 /** 1894 * @param growls the growls to set 1895 */ 1896 public void setGrowls(Growls growls) { 1897 checkMutable(true); 1898 this.growls = growls; 1899 } 1900 1901 /** 1902 * @return returns the refresh block object 1903 */ 1904 @BeanTagAttribute 1905 public BlockUI getRefreshBlockUI() { 1906 return refreshBlockUI; 1907 } 1908 1909 /** 1910 * Set the refresh BlockUI used with single element blocking 1911 * (such as ajax based element loading/updates) 1912 * 1913 * @param refreshBlockUI 1914 */ 1915 public void setRefreshBlockUI(BlockUI refreshBlockUI) { 1916 checkMutable(true); 1917 this.refreshBlockUI = refreshBlockUI; 1918 } 1919 1920 /** 1921 * @return returns the navigation block object 1922 */ 1923 @BeanTagAttribute 1924 public BlockUI getNavigationBlockUI() { 1925 return navigationBlockUI; 1926 } 1927 1928 /** 1929 * Set the navigation BlockUI used with single page blocking 1930 * (such as full page loading/saving) 1931 * 1932 * @param navigationBlockUI 1933 */ 1934 public void setNavigationBlockUI(BlockUI navigationBlockUI) { 1935 checkMutable(true); 1936 this.navigationBlockUI = navigationBlockUI; 1937 } 1938 1939 /** 1940 * Whether to use growls to show messages - info, warning and error 1941 * 1942 * <p>Growls use the messages contained in the message map. If enabled, info 1943 * messages in their entirety will be displayed in growls, for warning and 1944 * error messages a growl message will notify the user that these messages 1945 * exist on the page.</p> 1946 * 1947 * <p> If this setting is disabled, it is recommended that 1948 * infoMessage display be enabled for the page ValidationMessages bean in order to 1949 * display relevant information to the user. Note: the growl scripts are 1950 * built out in the PageGroup class.</p> 1951 * 1952 * @return the growlMessagingEnabled 1953 */ 1954 @BeanTagAttribute 1955 public boolean isGrowlMessagingEnabled() { 1956 return this.growlMessagingEnabled; 1957 } 1958 1959 /** 1960 * enable or disable showing of messages using growls 1961 * 1962 * @param growlMessagingEnabled the growlMessagingEnabled to set 1963 */ 1964 public void setGrowlMessagingEnabled(boolean growlMessagingEnabled) { 1965 checkMutable(true); 1966 this.growlMessagingEnabled = growlMessagingEnabled; 1967 } 1968 1969 /** 1970 * Indicates whether the form should be validated for dirtyness. 1971 * 1972 * <p>For FormView, it's necessary to validate when the user tries to navigate out of the form. If set, all the 1973 * InputFields will be validated on refresh, navigate, cancel or close Action or on form 1974 * unload and if dirty, displays a message and user can decide whether to continue with 1975 * the action or stay on the form. For lookup and inquiry, it's not needed to validate</p> 1976 * 1977 * @return true if dirty validation is set 1978 */ 1979 @BeanTagAttribute 1980 public boolean isApplyDirtyCheck() { 1981 return this.applyDirtyCheck; 1982 } 1983 1984 /** 1985 * @see View#isApplyDirtyCheck() 1986 */ 1987 public void setApplyDirtyCheck(boolean applyDirtyCheck) { 1988 checkMutable(true); 1989 this.applyDirtyCheck = applyDirtyCheck; 1990 } 1991 1992 /** 1993 * Returns whether the current view supports displaying <code>KualiCode</code>'s name as additional display value 1994 * 1995 * @return true if the current view supports 1996 */ 1997 @BeanTagAttribute 1998 public boolean isTranslateCodesOnReadOnlyDisplay() { 1999 return translateCodesOnReadOnlyDisplay; 2000 } 2001 2002 /** 2003 * Indicates whether the Name of the Code should be displayed when a property is of type <code>KualiCode</code> 2004 * 2005 * @param translateCodesOnReadOnlyDisplay indicates whether KualiCode's name should be included. 2006 */ 2007 public void setTranslateCodesOnReadOnlyDisplay(boolean translateCodesOnReadOnlyDisplay) { 2008 checkMutable(true); 2009 this.translateCodesOnReadOnlyDisplay = translateCodesOnReadOnlyDisplay; 2010 } 2011 2012 /** 2013 * Indicates whether the view allows read only fields to be specified on the request URL which will 2014 * override the view setting. 2015 * 2016 * <p>If enabled, the readOnlyFields request parameter can be sent to indicate fields that should be set read only</p> 2017 * 2018 * @return true if read only request overrides are allowed, false if not 2019 */ 2020 @BeanTagAttribute 2021 public boolean isSupportsRequestOverrideOfReadOnlyFields() { 2022 return supportsRequestOverrideOfReadOnlyFields; 2023 } 2024 2025 /** 2026 * @see View#isSupportsRequestOverrideOfReadOnlyFields() 2027 */ 2028 public void setSupportsRequestOverrideOfReadOnlyFields(boolean supportsRequestOverrideOfReadOnlyFields) { 2029 checkMutable(true); 2030 this.supportsRequestOverrideOfReadOnlyFields = supportsRequestOverrideOfReadOnlyFields; 2031 } 2032 2033 /** 2034 * Indicates whether the browser autocomplete functionality should be disabled for the 2035 * entire form (adds autocomplete="off"). 2036 * 2037 * <p>The browser's native autocomplete functionality can cause issues with security fields and also fields 2038 * with the UIF suggest widget enabled</p> 2039 * 2040 * @return true if the native autocomplete should be turned off for the form, false if not 2041 */ 2042 @BeanTagAttribute 2043 public boolean isDisableNativeAutocomplete() { 2044 return disableNativeAutocomplete; 2045 } 2046 2047 /** 2048 * @see View#isDisableNativeAutocomplete() 2049 */ 2050 public void setDisableNativeAutocomplete(boolean disableNativeAutocomplete) { 2051 checkMutable(true); 2052 this.disableNativeAutocomplete = disableNativeAutocomplete; 2053 } 2054 2055 /** 2056 * Enables functionality to bust the browsers cache by appending an unique cache key. 2057 * 2058 * <p>Since response headers are unreliable for preventing caching in all browsers, the 2059 * framework uses a technique for updating the URL to include an unique cache key. If the 2060 * HTML 5 History API is supported a parameter can be added to the URL which causes the browser 2061 * to not find the cached page when the user goes back. If not the framework falls back to using 2062 * a hash key and resubmitting using script to pull the latest</p> 2063 * 2064 * @return true if cache for the view should be disabled, false if not 2065 */ 2066 @BeanTagAttribute 2067 public boolean isDisableBrowserCache() { 2068 return disableBrowserCache; 2069 } 2070 2071 /** 2072 * @see View#isDisableBrowserCache() 2073 */ 2074 public void setDisableBrowserCache(boolean disableBrowserCache) { 2075 checkMutable(true); 2076 this.disableBrowserCache = disableBrowserCache; 2077 } 2078 2079 /** 2080 * Script that is executed at the beginning of page load (before any other script). 2081 * 2082 * <p>Many used to set server variables client side</p> 2083 * 2084 * @return pre load script 2085 */ 2086 @BeanTagAttribute 2087 public String getPreLoadScript() { 2088 return preLoadScript; 2089 } 2090 2091 /** 2092 * @see View#getPreLoadScript() 2093 */ 2094 public void setPreLoadScript(String preLoadScript) { 2095 checkMutable(true); 2096 this.preLoadScript = preLoadScript; 2097 } 2098 2099 /** 2100 * The theme which contains stylesheets for this view. 2101 * 2102 * @return ViewTheme 2103 */ 2104 @BeanTagAttribute 2105 public ViewTheme getTheme() { 2106 return theme; 2107 } 2108 2109 /** 2110 * @see View#getTheme() 2111 */ 2112 public void setTheme(ViewTheme theme) { 2113 checkMutable(true); 2114 this.theme = theme; 2115 } 2116 2117 /** 2118 * The stateObject's binding path, this will be used along with the StateMapping's statePropertyName to 2119 * determine what field in the model state information is stored in for this view. Used during View validation. 2120 * 2121 * @return stateObjectBindingPath path to the object storing state information 2122 */ 2123 @BeanTagAttribute 2124 public String getStateObjectBindingPath() { 2125 return stateObjectBindingPath; 2126 } 2127 2128 /** 2129 * The stateObject's binding path, this will be used along with the StateMapping's statePropertyName to 2130 * determine what field in the model state information is stored in for this view. Used during View validation. 2131 * 2132 * @param stateObjectBindingPath 2133 */ 2134 public void setStateObjectBindingPath(String stateObjectBindingPath) { 2135 checkMutable(true); 2136 this.stateObjectBindingPath = stateObjectBindingPath; 2137 } 2138 2139 /** 2140 * Gets the stateMapping. 2141 * 2142 * <p>The state mapping object is used to determine the state information for a view, 2143 * it must include an ordered list of states, and where to find the state information for the view. 2144 * A stateMapping must be set for state based validation to occur. When stateMapping information is 2145 * not included, the view's model is considered stateless and all constraints will apply regardless of their 2146 * state information or replacements (ie, they will function as they did in version 2.1).</p> 2147 * 2148 * @return information needed for state based validation, if null no state based validation 2149 * functionality will exist and configured constraints will apply regardless of state 2150 * @since 2.2 2151 */ 2152 @BeanTagAttribute 2153 public StateMapping getStateMapping() { 2154 return stateMapping; 2155 } 2156 2157 /** 2158 * @see View#getStateMapping() 2159 */ 2160 public void setStateMapping(StateMapping stateMapping) { 2161 checkMutable(true); 2162 this.stateMapping = stateMapping; 2163 } 2164 2165 /** 2166 * Returns the general context that is available before the apply model phase (during the 2167 * initialize phase) 2168 * 2169 * @return context map 2170 */ 2171 public Map<String, Object> getPreModelContext() { 2172 if (preModelContext == null) { 2173 Map<String, Object> context = new HashMap<String, Object>(); 2174 2175 context.put(UifConstants.ContextVariableNames.VIEW, this); 2176 context.put(UifConstants.ContextVariableNames.VIEW_HELPER, viewHelperService); 2177 2178 ViewTheme theme = getTheme(); 2179 if (theme != null) { 2180 context.put(UifConstants.ContextVariableNames.THEME_IMAGES, theme.getImageDirectory()); 2181 } 2182 2183 Map<String, String> properties = CoreApiServiceLocator.getKualiConfigurationService().getAllProperties(); 2184 context.put(UifConstants.ContextVariableNames.CONFIG_PROPERTIES, properties); 2185 context.put(UifConstants.ContextVariableNames.CONSTANTS, KRADConstants.class); 2186 context.put(UifConstants.ContextVariableNames.UIF_CONSTANTS, UifConstants.class); 2187 context.put(UifConstants.ContextVariableNames.USER_SESSION, GlobalVariables.getUserSession()); 2188 2189 preModelContext = Collections.unmodifiableMap(context); 2190 } 2191 2192 return preModelContext; 2193 } 2194 2195 /** 2196 * @see org.kuali.rice.krad.uif.component.ComponentBase#clone() 2197 */ 2198 @Override 2199 public View clone() throws CloneNotSupportedException { 2200 View viewCopy = (View) super.clone(); 2201 viewCopy.viewIndex = new ViewIndex(); 2202 return viewCopy; 2203 } 2204 2205 /** 2206 * {@inheritDoc} 2207 */ 2208 @Override 2209 public void completeValidation(ValidationTrace tracer) { 2210 tracer.addBean(this); 2211 2212 // Check for the presence of a valid item with an not-null EntryPageId 2213 boolean validPageId = false; 2214 if (getEntryPageId() != null) { 2215 for (int i = 0; i < getItems().size(); i++) { 2216 if (getEntryPageId().compareTo(getItems().get(i).getId()) == 0) { 2217 validPageId = true; 2218 } 2219 } 2220 } else { 2221 validPageId = true; 2222 } 2223 if (!validPageId) { 2224 String currentValues[] = {"entryPageId = " + getEntryPageId()}; 2225 tracer.createError("Items must contain an item with a matching id to entryPageId", currentValues); 2226 } 2227 2228 // Check to insure the view as not already been set 2229 if (tracer.getValidationStage() == ValidationTrace.START_UP) { 2230 if (getViewStatus().compareTo(ViewStatus.CREATED) != 0) { 2231 String currentValues[] = {"viewStatus = " + getViewStatus()}; 2232 tracer.createError("ViewStatus should not be set", currentValues); 2233 } 2234 } 2235 2236 // Check to insure the binding object path is a valid property 2237 boolean validDefaultBindingObjectPath = false; 2238 if (getDefaultBindingObjectPath() == null) { 2239 validDefaultBindingObjectPath = true; 2240 } else if (DataDictionary.isPropertyOf(getFormClass(), getDefaultBindingObjectPath())) { 2241 validDefaultBindingObjectPath = true; 2242 } 2243 if (!validDefaultBindingObjectPath) { 2244 String currentValues[] = 2245 {"formClass = " + getFormClass(), "defaultBindingPath = " + getDefaultBindingObjectPath()}; 2246 tracer.createError("DefaultBingdingObjectPath must be a valid property of the formClass", currentValues); 2247 } 2248 2249 // Check to insure the page is set if the view is a single page 2250 if (isSinglePageView()) { 2251 if (getPage() == null) { 2252 String currentValues[] = {"singlePageView = " + isSinglePageView(), "page = " + getPage()}; 2253 tracer.createError("Page must be set if singlePageView is true", currentValues); 2254 } 2255 for (int i = 0; i < getItems().size(); i++) { 2256 if (getItems().get(i).getClass() == PageGroup.class) { 2257 String currentValues[] = 2258 {"singlePageView = " + isSinglePageView(), "items(" + i + ") = " + getItems().get(i) 2259 .getClass()}; 2260 tracer.createError("Items cannot be pageGroups if singlePageView is true", currentValues); 2261 } 2262 } 2263 } 2264 2265 // Checks to insure the Growls are set if growl messaging is enabled 2266 if (isGrowlMessagingEnabled() == true && getGrowls() == null) { 2267 if (Validator.checkExpressions(this, "growls")) { 2268 String currentValues[] = 2269 {"growlMessagingEnabled = " + isGrowlMessagingEnabled(), "growls = " + getGrowls()}; 2270 tracer.createError("Growls cannot be null if Growl Messaging is enabled", currentValues); 2271 } 2272 } 2273 2274 // Checks that there are items present if the view is not a single page 2275 if (!isSinglePageView()) { 2276 if (getItems().size() == 0) { 2277 String currentValues[] = 2278 {"singlePageView = " + isSinglePageView(), "items.size = " + getItems().size()}; 2279 tracer.createWarning("Items cannot be empty if singlePageView is false", currentValues); 2280 } else { 2281 for (int i = 0; i < getItems().size(); i++) { 2282 if (getItems().get(i).getClass() != PageGroup.class) { 2283 String currentValues[] = 2284 {"singlePageView = " + isSinglePageView(), "items(" + i + ") = " + getItems().get(i) 2285 .getClass()}; 2286 tracer.createError("Items must be pageGroups if singlePageView is false", currentValues); 2287 } 2288 } 2289 } 2290 } 2291 super.completeValidation(tracer.getCopy()); 2292 } 2293 2294}