View Javadoc

1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   */
15  
16  package org.kuali.student.common.ui.client.configurable.mvc.sections;
17  
18  import java.util.ArrayList;
19  import java.util.HashSet;
20  import java.util.List;
21  
22  import org.kuali.student.common.assembly.data.Data;
23  import org.kuali.student.common.assembly.data.Data.Key;
24  import org.kuali.student.common.assembly.data.QueryPath;
25  import org.kuali.student.common.ui.client.application.Application;
26  import org.kuali.student.common.ui.client.configurable.mvc.CanProcessValidationResults;
27  import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
28  import org.kuali.student.common.ui.client.configurable.mvc.LayoutController;
29  import org.kuali.student.common.ui.client.configurable.mvc.ValidationEventBinding;
30  import org.kuali.student.common.ui.client.configurable.mvc.ValidationEventBindingImpl;
31  import org.kuali.student.common.ui.client.configurable.mvc.binding.ModelWidgetBinding;
32  import org.kuali.student.common.ui.client.configurable.mvc.binding.ModelWidgetBindingSupport;
33  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityComposite;
34  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityGroup;
35  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityGroupItem;
36  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityItem;
37  import org.kuali.student.common.ui.client.event.ContentDirtyEvent;
38  import org.kuali.student.common.ui.client.event.ValidateRequestEvent;
39  import org.kuali.student.common.ui.client.mvc.Callback;
40  import org.kuali.student.common.ui.client.mvc.Controller;
41  import org.kuali.student.common.ui.client.mvc.DataModel;
42  import org.kuali.student.common.ui.client.mvc.HasCrossConstraints;
43  import org.kuali.student.common.ui.client.mvc.ModelRequestCallback;
44  import org.kuali.student.common.ui.client.mvc.View;
45  import org.kuali.student.common.ui.client.widgets.field.layout.element.AbbrPanel;
46  import org.kuali.student.common.ui.client.widgets.field.layout.element.FieldElement;
47  import org.kuali.student.common.ui.client.widgets.field.layout.element.SpanPanel;
48  import org.kuali.student.common.ui.client.widgets.field.layout.layouts.FieldLayout;
49  import org.kuali.student.common.validation.dto.ValidationResultInfo;
50  import org.kuali.student.common.validation.dto.ValidationResultInfo.ErrorLevel;
51  
52  import com.google.gwt.core.client.GWT;
53  import com.google.gwt.user.client.ui.Widget;
54  
55  /**
56   * The base section is the base implementation of the section interface.
57   * @author Kuali Student
58   *
59   */
60  public abstract class BaseSection extends SpanPanel implements Section{
61  
62  	protected FieldLayout layout;
63  
64  	protected ArrayList<Section> sections = new ArrayList<Section>();
65  	protected ArrayList<FieldDescriptor> fields = new ArrayList<FieldDescriptor>();
66  	protected LayoutController layoutController = null;
67  	protected boolean isValidationEnabled = true;
68  	protected boolean isDirty = false;
69  
70  	/**
71  	 * Adds a field to this section.  Adds the field to this section's layout.
72  	 * Attaches an event handler for lost focus events on the field widget
73  	 * to validate against the metadata defined in its FieldDescriptor as well as firing events for dirty
74  	 * field handling.
75  	 * 
76  	 * Note if a field has been marked as hidden in the dictionary, this method will not add this field to the layout.
77  	 * If it is not visible the key returned by making call to addField is null.
78  	 * 
79  	 * @param field
80  	 * @return key/path of this field
81  	 */
82  	@Override
83  	public String addField(final FieldDescriptor fieldDescriptor) {
84  		String key = null;
85  		
86  		if (fieldDescriptor.isVisible()){
87  			fields.add(fieldDescriptor);
88  	        key = layout.addField(fieldDescriptor.getFieldElement());
89  	
90  	        ValidationEventBinding binding = new ValidationEventBindingImpl();
91  	        if(fieldDescriptor.getValidationRequestCallback()== null){
92  	            fieldDescriptor.setValidationCallBack(new Callback<Boolean>() {
93  	                @Override
94  	                public void exec(Boolean result) {
95  	            	    final ModelWidgetBinding mwb = fieldDescriptor.getModelWidgetBinding();
96  	            	    if (mwb != null) {
97  	            	        final Widget w = fieldDescriptor.getFieldWidget();
98  	            	        final String modelId = fieldDescriptor.getModelId();
99  	            	        final Controller parent;
100 	                        Controller findResult = LayoutController.findParentLayout(w);
101 	                        if(BaseSection.this instanceof View){
102 	                        	findResult = ((View)BaseSection.this).getController();
103 	                        }
104 	                        parent = findResult;
105 	                        if(parent != null){
106 	                        	if (modelId == null) {
107 	                        		parent.requestModel(new ModelRequestCallback<DataModel>() {
108 	
109 	                        			@Override
110 	                        			public void onModelReady(DataModel model) {
111 	                        				validateField(fieldDescriptor, model, parent);
112 	                                        
113 	                        				//Cross referenced field update:
114 	                        				HashSet<HasCrossConstraints> fds = Application.getApplicationContext().getCrossConstraint(null, Application.getApplicationContext().getParentPath()+fieldDescriptor.getFieldKey());
115 	                        				if(fds!=null){
116 	                        					for(HasCrossConstraints fd:fds){
117                         							fd.reprocessWithUpdatedConstraints();
118 	                        					}
119 	                        				}
120 	                        			}
121 	
122 	                        			@Override
123 	                        			public void onRequestFail(Throwable cause) {
124 	                        				GWT.log("Unable to retrieve model to validate " + fieldDescriptor.getFieldKey(), null);
125 	                        			}
126 	
127 	                        		});
128 	                        	} else {
129 	                        		parent.requestModel(modelId, new ModelRequestCallback<DataModel>() {
130 	
131 	                        			@Override
132 	                        			public void onModelReady(DataModel model) {
133 	                        				validateField(fieldDescriptor, model, parent);
134 	                        			}
135 	
136 	                        			@Override
137 	                        			public void onRequestFail(Throwable cause) {
138 	                        				GWT.log("Unable to retrieve model to validate " + fieldDescriptor.getFieldKey(), null);
139 	                        			}
140 	
141 	                        		});                        	}
142 	            	    	}
143 	                    } else {
144 	                        GWT.log(fieldDescriptor.getFieldKey() + " has no widget binding.", null);
145 	                    }
146 	                }
147 	            });
148 	        }
149 	        binding.bind(fieldDescriptor);
150 		}	       
151 	        
152         return key;
153 	}
154 
155 	private void validateField(
156 			final FieldDescriptor fieldDescriptor, final DataModel model, Controller controller) {
157 		Widget w = fieldDescriptor.getFieldWidget();
158 		ModelWidgetBinding mwb = fieldDescriptor.getModelWidgetBinding();
159 		if(fieldDescriptor.getFieldKey() != null){
160 			mwb.setModelValue(w, model, fieldDescriptor.getFieldKey());
161 			dirtyCheckField(fieldDescriptor, model);
162 			ValidateRequestEvent e = new ValidateRequestEvent();
163 			e.setFieldDescriptor(fieldDescriptor);
164 			e.setValidateSingleField(true);
165 			controller.fireApplicationEvent(e);
166 		}
167 	}
168 
169 	private void dirtyCheckField(FieldDescriptor fieldDescriptor, DataModel model){
170         QueryPath fieldPath = QueryPath.parse(fieldDescriptor.getFieldKey());
171 		QueryPath qPathDirty = fieldPath.subPath(0, fieldPath.size() - 1);
172 	    qPathDirty.add(ModelWidgetBindingSupport.RUNTIME_ROOT);
173 	    qPathDirty.add(ModelWidgetBindingSupport.DIRTY_PATH);
174 	    qPathDirty.add(fieldPath.get(fieldPath.size()-1));
175 	    Boolean dirty = false;
176 
177 	    if(ensureDirtyFlagPath(model.getRoot(), qPathDirty)){
178 	    	dirty = model.get(qPathDirty);
179 	    }
180 	    if(dirty){
181 	    	setIsDirty(true);
182 	    	fieldDescriptor.setDirty(true);
183 	    }
184 	}
185 
186     protected boolean ensureDirtyFlagPath(Data root, QueryPath path) {
187         Data current = root;
188         boolean containsFlag = false;
189         for (int i = 0; i < path.size(); i++) {
190             Key key = path.get(i);
191             if(i == path.size() - 1){
192             	containsFlag = current.containsKey(key);
193             	break;
194             }
195             Data d = current.get(key);
196             if (d == null) {
197                 containsFlag = false;
198                 break;
199             }
200             current = d;
201         }
202         return containsFlag;
203     }
204 
205 	/** 
206 	 * Adds a section to this section's layout.
207 	 * 
208 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#addSection(org.kuali.student.common.ui.client.configurable.mvc.sections.Section)
209 	 */
210 	@Override
211 	public String addSection(Section section) {
212 
213         section.setLayoutController(layoutController);
214 		sections.add(section);
215         String key = layout.addLayout(section.getLayout());
216         return key;
217 	}
218 
219 	/**
220 	 * Removes a section from this section's layout.
221 	 * 
222 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#removeSection(org.kuali.student.common.ui.client.configurable.mvc.sections.Section)
223 	 */
224 	@Override
225 	public void removeSection(Section section){
226 		sections.remove(section);
227 		layout.removeLayoutElement(section.getLayout());
228 	}
229 
230 	/**
231 	 * Clear all validation errors from the layout (removes all red highlight and error text shown on the
232 	 * screen)
233 	 */
234 	protected void clearValidation() {
235 		layout.clearValidation();
236 
237 	}
238 
239 	/**
240 	 * Gets all the fields in a section and its subsections.
241 	 * 
242 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#getFields()
243 	 */
244 	@Override
245 	public List<FieldDescriptor> getFields() {
246         List<FieldDescriptor> allFields = new ArrayList<FieldDescriptor>();
247         allFields.addAll(fields);
248 
249         for(Section ns: sections){
250             allFields.addAll(ns.getFields());
251         }
252         return allFields;
253 	}
254 
255 	/**
256 	 * Gets all the fields in this section, but does not include fields in its nested sections.
257 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#getUnnestedFields()
258 	 */
259 	@Override
260 	public List<FieldDescriptor> getUnnestedFields() {
261         return fields;
262 	}
263 
264 	/**
265 	 * Gets all nested sections contained in this section
266 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#getSections()
267 	 */
268 	@Override
269 	public List<Section> getSections() {
270 		return sections;
271 	}
272 
273 	/**
274 	 * Processes the validation results passed in and displays the appropriate message on the screen next
275 	 * to the corresponding field or section.  If clearAllValidation is true, it will clear all validation before
276 	 * displaying the errors (otherwise will append these errors to the ones already visible on the section).
277 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#processValidationResults(java.util.List, boolean)
278 	 */
279 	@Override
280 	public ErrorLevel processValidationResults(List<ValidationResultInfo> results, boolean clearAllValidation){
281 		if(clearAllValidation){
282 			this.clearValidation();
283 		}
284 		ErrorLevel status = ErrorLevel.OK;
285 
286 		if (isValidationEnabled){
287 
288 			for(FieldDescriptor f: this.fields){
289 
290 				if(f.hasHadFocus()){
291 					for(ValidationResultInfo vr: results){
292                         String vrElement = vr.getElement();
293                         if(vrElement.startsWith("/")){
294                             vrElement = vrElement.substring(1);
295                         }
296 						if(vrElement.equals(f.getFieldKey())){
297 							FieldElement element = f.getFieldElement();
298 							if (element != null){
299 								ErrorLevel fieldStatus = element.processValidationResult(vr);
300 								if(fieldStatus.getLevel() > status.getLevel()){
301 									status = fieldStatus;
302 								}
303 							}
304 						}
305 					}
306 				}
307 
308 				if(f.getFieldWidget() instanceof MultiplicityComposite ){
309 					MultiplicityComposite mc = (MultiplicityComposite) f.getFieldWidget();
310 
311 					//possibly return error state from processValidationResults to give composite title bar a separate color
312 	            	for(MultiplicityItem item: mc.getItems()){
313 	            		if(item.getItemWidget() instanceof Section && !item.isDeleted()){
314 	            			ErrorLevel fieldStatus = ((Section)item.getItemWidget()).processValidationResults(results, clearAllValidation);
315 							if(fieldStatus.getLevel() > status.getLevel()){
316 								status = fieldStatus;
317 							}
318 	            		}
319 	            	}
320 				}
321 				// TODO Can we do this without checking for instanceof  MG??
322 				if(f.getFieldWidget() instanceof MultiplicityGroup ){
323 					MultiplicityGroup mg = (MultiplicityGroup) f.getFieldWidget();
324 
325 					//possibly return error state from processValidationResults to give composite title bar a separate color
326 	            	for(MultiplicityGroupItem item: mg.getItems()){
327 	            		if(item.getItemWidget() instanceof Section && !item.isDeleted()){
328 	            			ErrorLevel fieldStatus = ((Section)item.getItemWidget()).processValidationResults(results, clearAllValidation);
329 							if(fieldStatus.getLevel() > status.getLevel()){
330 								status = fieldStatus;
331 							}
332 	            		}
333 	            	}
334 				}
335                 if (f.getFieldWidget() instanceof CanProcessValidationResults) {
336                     ErrorLevel fieldStatus = ((CanProcessValidationResults) f.getFieldWidget()).processValidationResults(f, results, clearAllValidation);
337                     if (fieldStatus.getLevel() > status.getLevel()) {
338                         status = fieldStatus;
339                     }
340                 }
341 			}
342 
343 	        for(Section s: sections){
344 	            ErrorLevel subsectionStatus = s.processValidationResults(results,clearAllValidation);
345 	            if(subsectionStatus.getLevel() > status.getLevel()){
346 	            	status = subsectionStatus;
347 	            }
348 	        }
349 		}
350 
351         return status;
352 	}
353 
354 	/**
355 	 * Same as processValidationResults(results, true)
356 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#processValidationResults(java.util.List)
357 	 */
358 	@Override
359 	public ErrorLevel processValidationResults(List<ValidationResultInfo> results) {
360 		return processValidationResults(results, true);
361 	}
362 
363 	/**
364 	 * Gets the defined controller for this section if one exists.
365 	 * 
366 	 * @see org.kuali.student.common.ui.client.configurable.mvc.HasLayoutController#getLayoutController()
367 	 */
368 	@Override
369 	public LayoutController getLayoutController() {
370 		return this.layoutController;
371 	}
372 
373 	@Override
374 	public void setLayoutController(LayoutController controller) {
375 		this.layoutController = controller;
376 	}
377 
378 
379 	/**
380 	 * Use this to add widgets to a sections layout.  DO NOT use the add(Widget widget) call.
381 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#addWidget(com.google.gwt.user.client.ui.Widget)
382 	 */
383 	@Override
384 	public String addWidget(Widget w) {
385 		return layout.addWidget(w);
386 	}
387 
388     /**
389      * Sets the fields has had focus flag.  This flag is used for determining if the user has interacted with
390      * any fields on the page or if the fields need to be assumed to have been interacted with.  Used for determining
391      * when validation is necessary on a particular section.
392      * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#setFieldHasHadFocusFlags(boolean)
393      */
394     public void setFieldHasHadFocusFlags(boolean hadFocus) {
395         for(FieldDescriptor f: fields){
396             f.setHasHadFocus(hadFocus);
397             if(f.getFieldWidget() instanceof MultiplicityComposite){
398 				MultiplicityComposite mc = (MultiplicityComposite) f.getFieldWidget();
399 
400             	for(MultiplicityItem item: mc.getItems()){
401             		if(item.getItemWidget() instanceof Section && !item.isDeleted()){
402             			((Section) item.getItemWidget()).setFieldHasHadFocusFlags(hadFocus);
403             		}
404             	}
405             }else if(f.getFieldWidget() instanceof MultiplicityGroup){
406             	MultiplicityGroup mg = (MultiplicityGroup) f.getFieldWidget();
407 
408             	for(MultiplicityGroupItem item: mg.getItems()){
409             		if(item.getItemWidget() instanceof Section && !item.isDeleted()){
410             			((Section) item.getItemWidget()).setFieldHasHadFocusFlags(hadFocus);
411             		}
412             	}
413             }
414         }
415 
416         for(Section s: sections){
417             s.setFieldHasHadFocusFlags(hadFocus);
418         }
419 
420     }
421 
422     /**
423      * A section can turn off all validation by setting this flag
424      * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#enableValidation(boolean)
425      */
426     @Override
427     public void enableValidation(boolean enableValidation) {
428     	this.isValidationEnabled = enableValidation;
429     }
430 
431     @Override
432     public boolean isValidationEnabled() {
433 		return isValidationEnabled;
434 	}
435 
436 	/**
437 	 * Update the model passed in with data from the fields on this section.  This method will use the 
438 	 * modelWidgetBinding defined in each of this sections fields to determine how to add this data to the
439 	 * model.
440 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#updateModel(org.kuali.student.common.ui.client.mvc.DataModel)
441 	 */
442 	@Override
443     public void updateModel(DataModel model){
444         SectionBinding.INSTANCE.setModelValue(this, model, "");
445     }
446 
447     /**
448      * Updates the section's fields with data from the model passed in.  This effects all the data input and
449      * display widgets on the particular section.  This method will use the 
450 	 * modelWidgetBinding defined in each of this sections fields to determine how to interpret data from the
451 	 * model and display it on the fields corresponding widget.
452 	 * 
453      * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#updateWidgetData(org.kuali.student.common.ui.client.mvc.DataModel)
454      */
455     @Override
456     public void updateWidgetData(DataModel model) {
457         SectionBinding.INSTANCE.setWidgetValue(this, model, "");
458     }
459 
460     /**
461      * Resets all the dirty and focus flags on fields.
462      * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#resetFieldInteractionFlags()
463      */
464     @Override
465     public void resetFieldInteractionFlags() {
466     	this.isDirty = false;
467         for(FieldDescriptor f: fields){
468             f.setDirty(false);
469             f.setHasHadFocus(false);
470         }
471 
472         for(Section s: sections){
473             s.resetFieldInteractionFlags();
474         }
475 
476     }
477 
478     @Override
479     public void resetDirtyFlags() {
480     	this.isDirty = false;
481         for(FieldDescriptor f: fields){
482             f.setDirty(false);
483         }
484 
485         for(Section s: sections){
486             s.resetDirtyFlags();
487         }
488     }
489 
490 
491 	/**
492 	 * Same as addSection except with an option user defined key (for retrieval of the section if necessary).
493 	 * 
494 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#addSection(java.lang.String, org.kuali.student.common.ui.client.configurable.mvc.sections.Section)
495 	 */
496 	@Override
497 	public String addSection(String key, Section section) {
498         sections.add(section);
499         section.getLayout().setKey(key);
500         String rkey = layout.addLayout(section.getLayout());
501         return rkey;
502 	}
503 
504 	@Override
505 	public FieldDescriptor getField(String fieldKey) {
506 		for(FieldDescriptor f: fields){
507 			if(f.getFieldKey().equals(fieldKey)){
508 				return f;
509 			}
510 		}
511 		return null;
512 	}
513 
514 	/**
515 	 * Gets this sections fieldLayout.  The fieldLayout is used to determine how sections will layout the
516 	 * ui and contains things such as the title and validation panels.
517 	 * 
518 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#getLayout()
519 	 */
520 	@Override
521 	public FieldLayout getLayout() {
522 		return layout;
523 	}
524 
525 	@Override
526 	public Section getSection(String sectionKey) {
527 		for(Section s: sections){
528 			if(s.getLayout().getKey().equals(sectionKey)){
529 				return s;
530 			}
531 		}
532 		return null;
533 	}
534 
535 	@Override
536 	public void removeField(String fieldKey) {
537 		int index = 0;
538 		boolean found = false;
539 		for(FieldDescriptor f: fields){
540 			if(f.getFieldKey().equals(fieldKey)){
541 				index = fields.indexOf(f);
542 				found = true;
543 				break;
544 			}
545 		}
546 		if(found){
547 			fields.remove(index);
548 		}
549 		layout.removeLayoutElement(fieldKey);
550 
551 	}
552 
553 	@Override
554 	public void removeField(FieldDescriptor field) {
555 
556 		fields.remove(field);
557 		layout.removeLayoutElement(field.getFieldKey());
558 
559 	}
560 
561 	@Override
562 	public void removeSection(String sectionKey) {
563 		int index = 0;
564 		boolean found = false;
565 		for(Section s: sections){
566 			if(s.getLayout().getKey().equals(sectionKey)){
567 				index = sections.indexOf(s);
568 				found = true;
569 				break;
570 			}
571 		}
572 		if(found){
573 			sections.remove(index);
574 		}
575 		layout.removeLayoutElement(sectionKey);
576 
577 	}
578 
579 	@Override
580 	public void removeWidget(Widget widget) {
581 		layout.removeLayoutElement(widget);
582 
583 	}
584 
585 	@Override
586 	public void removeWidget(String key) {
587 		layout.removeLayoutElement(key);
588 
589 	}
590 
591 	/**
592 	 * Returns true if this this section is considered dirty (the user may have entered data into this
593 	 * section)
594 	 * 
595 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#isDirty()
596 	 */
597 	public boolean isDirty(){
598 		if(!this.isDirty){
599 			//Check child sections for dirtyness
600 			for(Section s: sections){
601 				if(s.isDirty()){
602 					setIsDirty(true);
603 					break;
604 				}
605 			}
606 		}
607 		return isDirty;
608 	}
609 
610     public void setIsDirty(boolean state) {
611 		//Should this trust layoutController to be already set?
612     	if (layoutController == null){
613     		layoutController = LayoutController.findParentLayout(layout);
614     	}
615     	if (isDirty != state){
616         	isDirty = state;
617 	    	if (layoutController != null && isDirty){
618 	    		layoutController.fireApplicationEvent(new ContentDirtyEvent());
619 	    	}
620     	}
621     }
622 
623 	/**
624 	 * DO NOT use this method for adding sections, fields, or widgets to sections
625 	 */
626 	@Override
627 	public void add(Widget w) {
628 		super.add(w);
629 	}
630 
631 	/**
632 	 * Adds a style to this section's underlying layout.
633 	 * @see com.google.gwt.user.client.ui.UIObject#addStyleName(java.lang.String)
634 	 */
635 	@Override
636 	public void addStyleName(String style) {
637 		layout.addStyleName(style);
638 	}
639 
640 	@Override
641 	public void setStyleName(String style) {
642 		layout.setStyleName(style);
643 	}
644 
645 	/**
646 	 * Sets instructions for this entire section (appears next to section title)
647 	 * @param html
648 	 */
649 	public void setInstructions(String html){
650 		layout.setInstructions(html);
651 	}
652 
653 	/**
654 	 * Sets help for this entire section (appears next to section title)
655 	 * @param html
656 	 */
657 	public void setHelp(String html){
658 		layout.setHelp(html);
659 	}
660 	
661 	/**
662 	 * Sets required for this entire section (appears next to section title)
663 	 * @param required
664 	 */
665 	public void setRequired(AbbrPanel required){
666 		layout.setRequired(required);
667 	}
668 }