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