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