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