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  /*
17   * Copyright 2009 The Kuali Foundation Licensed under the
18   * Educational Community License, Version 2.0 (the "License"); you may
19   * not use this file except in compliance with the License. You may
20   * obtain a copy of the License at
21   *
22   * http://www.osedu.org/licenses/ECL-2.0
23   *
24   * Unless required by applicable law or agreed to in writing,
25   * software distributed under the License is distributed on an "AS IS"
26   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
27   * or implied. See the License for the specific language governing
28   * permissions and limitations under the License.
29   */
30  package org.kuali.student.lum.lu.ui.course.client.configuration;
31  
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.HashMap;
35  import java.util.List;
36  import java.util.Map;
37  
38  import org.kuali.student.common.ui.client.application.Application;
39  import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
40  import org.kuali.student.common.ui.client.configurable.mvc.SectionTitle;
41  import org.kuali.student.common.ui.client.configurable.mvc.binding.HasDataValueBinding;
42  import org.kuali.student.common.ui.client.configurable.mvc.binding.ListOfStringBinding;
43  import org.kuali.student.common.ui.client.configurable.mvc.binding.ModelWidgetBinding;
44  import org.kuali.student.common.ui.client.configurable.mvc.binding.ModelWidgetBindingSupport;
45  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.CompositeConditionOperator;
46  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityConfiguration;
47  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityFieldConfiguration;
48  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityFieldWidgetInitializer;
49  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.SwapCompositeCondition;
50  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.SwapCompositeConditionFieldConfig;
51  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.SwapCondition;
52  import org.kuali.student.common.ui.client.configurable.mvc.sections.BaseSection;
53  import org.kuali.student.common.ui.client.configurable.mvc.sections.CollapsableSection;
54  import org.kuali.student.common.ui.client.configurable.mvc.sections.GroupSection;
55  import org.kuali.student.common.ui.client.configurable.mvc.sections.MultiplicitySection;
56  import org.kuali.student.common.ui.client.configurable.mvc.sections.Section;
57  import org.kuali.student.common.ui.client.configurable.mvc.sections.SwapEventHandler;
58  import org.kuali.student.common.ui.client.configurable.mvc.sections.SwapSection;
59  import org.kuali.student.common.ui.client.configurable.mvc.sections.VerticalSection;
60  import org.kuali.student.common.ui.client.configurable.mvc.views.SectionView;
61  import org.kuali.student.common.ui.client.configurable.mvc.views.VerticalSectionView;
62  import org.kuali.student.common.ui.client.mvc.Controller;
63  import org.kuali.student.common.ui.client.mvc.DataModel;
64  import org.kuali.student.common.ui.client.mvc.DataModelDefinition;
65  import org.kuali.student.common.ui.client.mvc.HasDataValue;
66  import org.kuali.student.common.ui.client.mvc.View;
67  import org.kuali.student.common.ui.client.widgets.KSButton;
68  import org.kuali.student.common.ui.client.widgets.KSButtonAbstract.ButtonStyle;
69  import org.kuali.student.common.ui.client.widgets.KSCharCount;
70  import org.kuali.student.common.ui.client.widgets.KSCheckBox;
71  import org.kuali.student.common.ui.client.widgets.KSDropDown;
72  import org.kuali.student.common.ui.client.widgets.ListOfStringWidget;
73  import org.kuali.student.common.ui.client.widgets.field.layout.element.MessageKeyInfo;
74  import org.kuali.student.common.ui.client.widgets.field.layout.element.SpanPanel;
75  import org.kuali.student.common.ui.client.widgets.field.layout.layouts.FieldLayoutComponent;
76  import org.kuali.student.common.ui.client.widgets.list.KSLabelList;
77  import org.kuali.student.common.ui.client.widgets.list.KSSelectItemWidgetAbstract;
78  import org.kuali.student.common.ui.client.widgets.list.KSSelectedList;
79  import org.kuali.student.common.ui.client.widgets.list.impl.SimpleListItems;
80  import org.kuali.student.common.ui.client.widgets.search.KSPicker;
81  import org.kuali.student.core.comments.ui.client.widgets.commenttool.CommentTool;
82  import org.kuali.student.core.comments.ui.client.widgets.decisiontool.DecisionPanel;
83  import org.kuali.student.core.document.ui.client.widgets.documenttool.DocumentTool;
84  import org.kuali.student.core.workflow.ui.client.views.CollaboratorSectionView;
85  import org.kuali.student.lum.common.client.lo.LOBuilder;
86  import org.kuali.student.lum.common.client.lo.LOBuilderBinding;
87  import org.kuali.student.lum.common.client.lo.LOPicker;
88  import org.kuali.student.lum.common.client.lo.OutlineNode;
89  import org.kuali.student.lum.common.client.lu.LUUIConstants;
90  import org.kuali.student.lum.lu.assembly.data.client.constants.base.RichTextInfoConstants;
91  import org.kuali.student.lum.lu.assembly.data.client.constants.orch.CreditCourseActivityConstants;
92  import org.kuali.student.lum.lu.assembly.data.client.constants.orch.CreditCourseConstants;
93  import org.kuali.student.lum.lu.assembly.data.client.constants.orch.CreditCourseJointsConstants;
94  import org.kuali.student.lum.lu.ui.course.client.controllers.CourseProposalController;
95  import org.kuali.student.lum.lu.ui.course.client.requirements.CourseRequirementsViewController;
96  import org.kuali.student.r1.common.assembly.data.Data;
97  import org.kuali.student.r1.common.assembly.data.Data.Value;
98  import org.kuali.student.r1.common.assembly.data.Metadata;
99  import org.kuali.student.r1.common.assembly.data.QueryPath;
100 import org.kuali.student.r1.core.statement.dto.StatementTypeInfo;
101 import org.kuali.student.r2.common.dto.DtoConstants;
102 
103 import com.google.gwt.core.client.GWT;
104 import com.google.gwt.event.dom.client.ClickEvent;
105 import com.google.gwt.event.dom.client.ClickHandler;
106 import com.google.gwt.event.logical.shared.ValueChangeEvent;
107 import com.google.gwt.event.logical.shared.ValueChangeHandler;
108 import com.google.gwt.user.client.ui.Widget;
109 
110 
111 /**
112  * This is the configuration factory class for creating a proposal.
113  *
114  * @author Kuali Student Team
115  */
116 public class CourseProposalConfigurer extends AbstractCourseConfigurer {
117 
118     protected boolean WITH_DIVIDER = true;
119     protected boolean NO_DIVIDER = false;
120   
121     public static final String PROPOSAL_PATH = "proposal";
122     public static final String PROPOSAL_TITLE_PATH = "proposal/name";
123     public static final String COURSE_TITLE_PATH = "/courseTitle";
124    
125 
126     protected DocumentTool documentTool;    
127     protected CourseSummaryConfigurer summaryConfigurer;
128 
129     protected List<StatementTypeInfo> stmtTypes;
130     
131     public static final String COURSE = "";
132 
133     public enum CourseSections {
134         CLU_BEGIN, PEOPLE_PERMISSONS, SUMMARY, AUTHORS_RATIONALE, GOVERNANCE, COURSE_LOGISTICS, COURSE_INFO, LEARNING_OBJECTIVES,
135         COURSE_REQUISITES, ACTIVE_DATES, FINANCIALS, ATTACHMENTS, COMMENTS, DECISIONS, DOCUMENTS,
136         PROGRAM_INFO, ASSEMBLER_TEST, WF_APPROVE_DIALOG
137     }
138 
139     public void setStatementTypes(List<StatementTypeInfo> stmtTypes) {
140         this.stmtTypes = stmtTypes;
141     }
142 
143     /**
144      * Sets up all the views, sections, and views of the CourseProposalController.  This should be called
145      * once for initialization and setup per CourseProposalController instance.
146      * 
147      * @param layout
148      */
149     public void configure(final CourseProposalController layout) {
150     	type = "course";
151         state = DtoConstants.STATE_DRAFT;
152     	groupName = LUUIConstants.COURSE_GROUP_NAME;
153 
154     	if (modelDefinition.getMetadata().isCanEdit()) {
155         	addCluStartSection(layout);
156             String sections = getLabel(LUUIConstants.COURSE_SECTIONS);
157 
158             //ProposalInformation
159             //layout.addSection(new String[] {editTabLabel, getLabel(LUConstants.PROPOSAL_INFORMATION_LABEL_KEY)}, generateAuthorsRationaleSection());
160 
161             layout.addMenu(sections);
162             
163             
164             //Course Content
165             layout.addMenuItem(sections, (SectionView)generateCourseInfoSection(initSectionView(CourseSections.COURSE_INFO, LUUIConstants.INFORMATION_LABEL_KEY)));
166             layout.addMenuItem(sections, (SectionView)generateGovernanceSection(initSectionView(CourseSections.GOVERNANCE, LUUIConstants.GOVERNANCE_LABEL_KEY)));
167             layout.addMenuItem(sections, (SectionView)generateCourseLogisticsSection(initSectionView(CourseSections.COURSE_LOGISTICS, LUUIConstants.LOGISTICS_LABEL_KEY)));
168             layout.addMenuItem(sections, generateLearningObjectivesSection());
169 
170             //Student Eligibility
171             layout.addMenuItem(sections, generateCourseRequisitesSection(layout,true));
172 
173             //Administrative
174             layout.addMenuItem(sections, (SectionView)generateActiveDatesSection(initSectionView(CourseSections.ACTIVE_DATES, LUUIConstants.ACTIVE_DATES_LABEL_KEY)));
175             layout.addMenuItem(sections, (SectionView)generateFinancialsSection(initSectionView(CourseSections.FINANCIALS, LUUIConstants.FINANCIALS_LABEL_KEY)));
176             
177             //Authors & Collaborators
178             layout.addMenuItem(sections, new CollaboratorSectionView(CourseSections.PEOPLE_PERMISSONS, getLabel(LUUIConstants.SECTION_AUTHORS_AND_COLLABORATORS), COURSE_PROPOSAL_MODEL));
179             
180             //Documents
181             documentTool = new DocumentTool(LUUIConstants.REF_DOC_RELATION_PROPOSAL_TYPE,CourseSections.DOCUMENTS, getLabel(LUUIConstants.TOOL_DOCUMENTS_LABEL_KEY));
182             documentTool.setModelDefinition((DataModelDefinition)modelDefinition);
183             layout.addMenuItem(sections, documentTool);
184             
185             //Summary
186             summaryConfigurer = GWT.create(CourseSummaryConfigurer.class);
187             summaryConfigurer.init(type, state, groupName,(DataModelDefinition)modelDefinition, stmtTypes, (Controller)layout, COURSE_PROPOSAL_MODEL);
188             layout.addSpecialMenuItem(summaryConfigurer.generateProposalSummarySection(true), "Review and Submit");
189             
190             //Add common buttons to sections except for sections with specific button behavior
191             List<Enum<?>> excludedViews = new ArrayList<Enum<?>>();
192             excludedViews.add(CourseSections.DOCUMENTS);
193             excludedViews.add(CourseSections.COURSE_REQUISITES);
194             layout.addCommonButton(LUUIConstants.COURSE_SECTIONS, layout.getSaveButton(), excludedViews);
195             layout.addCommonButton(LUUIConstants.COURSE_SECTIONS, layout.getCancelButton(CourseSections.SUMMARY), excludedViews);
196 
197             //Specific buttons for certain views
198             //TODO people and permissions will use a different button than continue
199             layout.addButtonForView(CourseSections.DOCUMENTS, getContinueButton(layout));
200         }
201         else{
202              summaryConfigurer = GWT.create(CourseSummaryConfigurer.class);
203              summaryConfigurer.init(type, state, groupName, (DataModelDefinition)modelDefinition, stmtTypes, (Controller)layout, COURSE_PROPOSAL_MODEL);
204         	 layout.removeMenuNavigation();
205              layout.addView(summaryConfigurer.generateProposalSummarySection(false));
206         }
207         layout.showPrint(true);
208         layout.setDefaultView(CourseSections.SUMMARY);
209         layout.addContentWidget(layout.getWfUtilities().getProposalStatusLabel());
210         final CommentTool commentTool = new CommentTool(CourseSections.COMMENTS, getLabel(LUUIConstants.TOOL_COMMENTS_LABEL_KEY), "kuali.comment.type.generalRemarks", "Proposal Comments");
211         commentTool.setController(layout);
212         
213         layout.addContentWidget(new KSButton("Comments", ButtonStyle.DEFAULT_ANCHOR, new ClickHandler() {
214             
215             @Override
216             public void onClick(ClickEvent event) {
217                 commentTool.show();
218             }
219         }));
220 
221         
222         final DecisionPanel decisionPanel = new DecisionPanel(CourseSections.DECISIONS, getLabel(LUUIConstants.TOOL_DECISION_LABEL_KEY), "kuali.comment.type.generalRemarks");
223         layout.addView(decisionPanel);
224         layout.addContentWidget(new KSButton("Decisions", ButtonStyle.DEFAULT_ANCHOR, new ClickHandler() {
225 
226             @Override
227             public void onClick(ClickEvent event) {
228             	decisionPanel.show();
229             }
230         }));
231         
232     }
233     
234     protected KSButton getContinueButton(final CourseProposalController layout){
235         return new KSButton("Continue", new ClickHandler(){
236                     public void onClick(ClickEvent event) {
237                     	layout.showNextViewOnMenu();
238                     }
239                 });
240     }
241 
242     public void addCluStartSection(CourseProposalController layout) {
243         VerticalSectionView section = initSectionView(CourseSections.CLU_BEGIN, LUUIConstants.START_LABEL_KEY);
244         section.setController(layout);
245         addField(section, PROPOSAL_TITLE_PATH, generateMessageInfo(LUUIConstants.PROPOSAL_TITLE_LABEL_KEY));
246         addField(section, COURSE + "/" + COURSE_TITLE, generateMessageInfo(LUUIConstants.COURSE_TITLE_LABEL_KEY));
247         //addField(section, "proposal/rationale", generateMessageInfo(LUConstants.PROPOSAL_RATIONALE_LABEL_KEY));
248         //addField(section, PROPOSAL + "/" + PROPOSER_PERSON, generateMessageInfo(LUConstants.PROPOSAL_PERSON_LABEL_KEY), new PersonList()) ;
249         layout.addStartViewPopup(section);
250         layout.getStartPopup().setMaxHeight(600);
251     }
252 
253     protected View generateCourseRequisitesSection(Controller layout, boolean showSaveButtons) {
254         return new CourseRequirementsViewController(layout, getLabel(LUUIConstants.REQUISITES_LABEL_KEY), CourseSections.COURSE_REQUISITES, false, showSaveButtons);
255     }
256 
257     protected Section generateActiveDatesSection(Section section) {
258         //Add this field and hide it so it is available for cross field validation 
259     	FieldDescriptor fd = addField(section, PROPOSAL_PATH + "/" + PREV_START_TERM, generateMessageInfo(LUUIConstants.PROPOSAL_PREV_START_TERM));
260         fd.getFieldWidget().setVisible(false);
261         fd.hideLabel();
262         
263     	addField(section, COURSE + "/" + START_TERM, generateMessageInfo(LUUIConstants.START_TERM_LABEL_KEY));
264     	
265         addField(section, COURSE + "/" + PILOT_COURSE, generateMessageInfo(LUUIConstants.PILOT_COURSE_LABEL_KEY), new KSCheckBox(getLabel(LUUIConstants.PILOT_COURSE_TEXT_LABEL_KEY))).setIgnoreShowRequired(true);
266         addField(section, COURSE + "/" + END_TERM, generateMessageInfo(LUUIConstants.END_TERM_LABEL_KEY)).setIgnoreShowRequired(true);
267 
268         return section;
269     }
270     
271     protected VerticalSection generateActiveDateEndSection() {
272         VerticalSection endDate = initSection(getH3Title(LUUIConstants.END_DATE_LABEL_KEY), WITH_DIVIDER);
273         addField(endDate, COURSE + "/" + EXPIRATION_DATE, generateMessageInfo(LUUIConstants.EXPIRATION_DATE_LABEL_KEY));
274         return endDate;
275     }
276 
277     protected VerticalSection generateActiveDateStartSection() {
278         VerticalSection startDate = initSection(getH3Title(LUUIConstants.START_DATE_LABEL_KEY), WITH_DIVIDER);
279         addField(startDate, COURSE + "/" + CreditCourseConstants.EFFECTIVE_DATE, generateMessageInfo(LUUIConstants.EFFECTIVE_DATE_LABEL_KEY));
280         return startDate;
281     }
282 
283     protected Section generateGovernanceSection(Section section) {
284         addField(section, COURSE + "/" + CAMPUS_LOCATIONS, generateMessageInfo(LUUIConstants.CAMPUS_LOCATION_LABEL_KEY));
285         addField(section, COURSE + "/" + CURRICULUM_OVERSIGHT_ORGS_, generateMessageInfo(LUUIConstants.ACADEMIC_SUBJECT_ORGS_KEY));
286         addField(section, COURSE + "/" + ADMIN_ORGS, generateMessageInfo(LUUIConstants.ADMIN_ORG_LABEL_KEY));
287 
288         return section;
289     }
290 
291     public Section generateCourseInfoSection(Section section) {
292         addField(section, PROPOSAL_TITLE_PATH, generateMessageInfo(LUUIConstants.PROPOSAL_TITLE_LABEL_KEY));
293         addField(section, COURSE + "/" + COURSE_TITLE, generateMessageInfo(LUUIConstants.COURSE_TITLE_LABEL_KEY));
294         addField(section, COURSE + "/" + TRANSCRIPT_TITLE, generateMessageInfo(LUUIConstants.SHORT_TITLE_LABEL_KEY), new KSCharCount(getMetaData(COURSE + "/" + TRANSCRIPT_TITLE)));
295         section.addSection(generateCourseNumberSection());
296     	FieldDescriptor instructorsFd = addField(section, COURSE + "/" + INSTRUCTORS, generateMessageInfo(LUUIConstants.INSTRUCTORS_LABEL_KEY));
297         instructorsFd.setWidgetBinding(new KeyListModelWigetBinding("personId"));
298 
299         section.addSection(generateDescriptionRationaleSection());
300         
301         return section;
302     }
303             
304 
305     protected GroupSection generateCourseNumberSection() {
306 
307         //COURSE NUMBER
308         GroupSection courseNumber = new GroupSection(getH4Title(""));
309         courseNumber.addStyleName(LUUIConstants.STYLE_SECTION);
310         courseNumber.addStyleName(LUUIConstants.STYLE_SECTION_DIVIDER);
311         addField(courseNumber, COURSE + "/" + SUBJECT_AREA, generateMessageInfo(LUUIConstants.SUBJECT_CODE_LABEL_KEY));
312         addField(courseNumber, COURSE + "/" + COURSE_NUMBER_SUFFIX, generateMessageInfo(LUUIConstants.COURSE_NUMBER_LABEL_KEY));
313 //        addField(courseNumber, COURSE + "/" + SUBJECT_AREA);
314 //        addField(courseNumber, COURSE + "/" + COURSE_NUMBER_SUFFIX);
315 
316         courseNumber.addSection(generateCrossListed_Ver_Joint_Section());
317 
318         return courseNumber;
319     }
320 
321     protected CollapsableSection generateCrossListed_Ver_Joint_Section() {
322         CollapsableSection result = new CollapsableSection(getLabel(LUUIConstants.CL_V_J_LABEL_KEY));
323 
324 //        addField(result, COURSE + "/" + CROSS_LISTINGS, null, new CrossListedList(COURSE + "/" + CROSS_LISTINGS));
325 //        addField(result, COURSE + "/" + JOINTS, null, new OfferedJointlyList(COURSE + "/" + JOINTS));
326 //        addField(result, COURSE + "/" + VERSIONS, null, new VersionCodeList(COURSE + "/" + VERSIONS));
327         SpanPanel crslabelpan = new SpanPanel();
328         crslabelpan.setStyleName("ks-multiplicity-section-label");
329         crslabelpan.setHTML("Cross Listed Courses");
330         crslabelpan.setVisible(true);
331         result.addWidget(crslabelpan);
332         addMultiplicityFields(result, COURSE + QueryPath.getPathSeparator() + CROSS_LISTINGS,
333                 LUUIConstants.ADD_CROSS_LISTED_LABEL_KEY,
334                 LUUIConstants.CROSS_LISTED_ITEM_LABEL_KEY,
335                 Arrays.asList(
336                         new MultiplicityFieldConfig(
337                                 SUBJECT_AREA, 
338                                 LUUIConstants.SUBJECT_CODE_LABEL_KEY, null, null, true),
339                         new MultiplicityFieldConfig(
340                                 COURSE_NUMBER_SUFFIX, 
341                                 LUUIConstants.COURSE_NUMBER_LABEL_KEY, null, null, true)),
342                         null,
343                         null,0);
344         SpanPanel jntlabelpan = new SpanPanel();
345         jntlabelpan.setStyleName("ks-multiplicity-section-label");
346         jntlabelpan.setHTML("Jointly Offered Courses");
347         jntlabelpan.setVisible(true);
348         result.addWidget(jntlabelpan);
349         addMultiplicityFields(result, COURSE + QueryPath.getPathSeparator() + JOINTS,
350                 LUUIConstants.ADD_EXISTING_LABEL_KEY,
351                 LUUIConstants.JOINT_OFFER_ITEM_LABEL_KEY,
352                 Arrays.asList(
353                         new MultiplicityFieldConfig(
354                                 CreditCourseJointsConstants.COURSE_ID, 
355                                 LUUIConstants.COURSE_NUMBER_OR_TITLE_LABEL_KEY, null, null, true)),
356                                 null,
357                                 null,0);
358         SpanPanel vsnlabelpan = new SpanPanel();
359         vsnlabelpan.setStyleName("ks-multiplicity-section-label");
360         vsnlabelpan.setHTML("Version Codes");
361         vsnlabelpan.setVisible(true);
362         result.addWidget(vsnlabelpan);
363         addMultiplicityFields(result, COURSE + QueryPath.getPathSeparator() + VERSIONS,
364                 LUUIConstants.ADD_VERSION_CODE_LABEL_KEY,
365                 LUUIConstants.VERSION_CODE_LABEL_KEY,
366                 Arrays.asList(
367                         new MultiplicityFieldConfig(
368                                 "variationCode", 
369                                 LUUIConstants.VERSION_CODE_LABEL_KEY, null, null, true), 
370                         new MultiplicityFieldConfig(
371                                 "variationTitle", 
372                                 LUUIConstants.TITLE_LABEL_KEY, null, null, true)
373                 ),
374                 null,
375                 null,0);
376         if(this.getClass().getName().contains("CourseProposalConfigurer"))
377             result.getLayout().setVisible(true);
378          else {
379                result.getLayout().setVisible(false);
380          }
381         return result;
382     }
383     
384     protected void addFeeMultiplicityFields(Section section,  
385             String path, String addItemlabelMessageKey,
386             String itemLabelMessageKey, List<MultiplicityFieldConfig> fieldConfigs,
387             Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition,
388             List<String> deletionParentKeys) {
389         MultiplicityConfiguration config = setupMultiplicityConfig(
390                 MultiplicityConfiguration.MultiplicityType.GROUP,
391                 MultiplicityConfiguration.StyleType.TOP_LEVEL_GROUP,
392                 path, addItemlabelMessageKey, itemLabelMessageKey,
393                 fieldConfigs, swappableFieldsDefinition, deletionParentKeys);
394         MultiplicitySection ms = null;
395         ms = new MultiplicitySection(config, swappableFieldsDefinition, deletionParentKeys);
396         section.addSection(ms);
397 
398     }
399     
400     protected MultiplicityConfiguration setupMultiplicityConfig(
401             MultiplicityConfiguration.MultiplicityType multiplicityType,
402             MultiplicityConfiguration.StyleType styleType,
403             String path, String addItemlabelMessageKey,
404             String itemLabelMessageKey, List<MultiplicityFieldConfig> fieldConfigs,
405             Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition,
406             List<String> deletionParentKeys) {
407         QueryPath parentPath = QueryPath.concat(path);
408         MultiplicityConfiguration config = new MultiplicityConfiguration(multiplicityType,
409                 styleType, getMetaData(parentPath.toString()));
410         config.setAddItemLabel(getLabel(addItemlabelMessageKey));
411         config.setItemLabel(getLabel(itemLabelMessageKey));
412         config.setUpdateable(true);
413 
414         FieldDescriptor parentFd = buildMultiplicityParentFieldDescriptor(path, getLabel(itemLabelMessageKey), null);
415         config.setParent(parentFd);
416 
417         if (fieldConfigs != null) {
418             for (MultiplicityFieldConfig fieldConfig : fieldConfigs) {
419                 MultiplicityFieldConfiguration fc = buildMultiplicityFD(fieldConfig.getFieldKey(),
420                         fieldConfig.getLabelKey(), parentPath.toString());
421                 config.addFieldConfiguration(fc);
422                 if (fieldConfig.isNextLine()) {
423                     config.nextLine();
424                 }
425             }
426         }
427         return config;
428     }
429 
430     protected MultiplicitySection addMultiplicityFields(Section section,  
431             String path, String addItemlabelMessageKey,
432             String itemLabelMessageKey, List<MultiplicityFieldConfig> fieldConfigs,
433             Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition,
434             List<String> deletionParentKeys,int defaultItemsCreated) {
435         MultiplicityConfiguration config = setupMultiplicityConfig(
436                 MultiplicityConfiguration.MultiplicityType.GROUP,
437                 MultiplicityConfiguration.StyleType.TOP_LEVEL_GROUP,
438                 path, addItemlabelMessageKey, itemLabelMessageKey,
439                 fieldConfigs, swappableFieldsDefinition, deletionParentKeys);
440         config.setDefaultItemsCreated(defaultItemsCreated);
441         MultiplicitySection ms = null;
442         ms = new MultiplicitySection(config, swappableFieldsDefinition, deletionParentKeys);
443         section.addSection(ms);
444         return ms;
445     }
446 
447     protected Metadata getMetaData(String fieldKey) {
448         return modelDefinition.getMetadata(QueryPath.concat(fieldKey));
449     }
450 
451     protected MultiplicityFieldConfiguration buildMultiplicityFD(
452             String fieldKey, String labelKey, String parentPath) {
453 
454         QueryPath fieldPath = QueryPath.concat(parentPath, QueryPath.getWildCard(), fieldKey);
455         Metadata meta = modelDefinition.getMetadata(fieldPath);
456 
457         MultiplicityFieldConfiguration fd = new MultiplicityFieldConfiguration(
458                 fieldPath.toString(), generateMessageInfo(labelKey), meta, null);
459         
460 
461         return fd;
462 
463     }
464 
465     protected FieldDescriptor buildMultiplicityParentFieldDescriptor(String fieldKey, String messageKey, String parentPath) {
466         QueryPath path = QueryPath.concat(parentPath, fieldKey);
467         Metadata meta = modelDefinition.getMetadata(path);
468 
469         FieldDescriptor fd = new FieldDescriptor(path.toString(), generateMessageInfo(messageKey), meta);
470         fd.hideLabel();
471         return fd;
472     }
473 
474     protected VerticalSection generateCourseInfoShortTitleSection() {
475         VerticalSection shortTitle = initSection(getH3Title(LUUIConstants.SHORT_TITLE_LABEL_KEY), WITH_DIVIDER);
476         addField(shortTitle, COURSE + "/" + TRANSCRIPT_TITLE, null);
477         return shortTitle;
478     }
479 
480     protected VerticalSection generateLongTitleSection() {
481         VerticalSection longTitle = initSection(getH3Title(LUUIConstants.TITLE_LABEL_KEY), WITH_DIVIDER);
482         addField(longTitle, COURSE + "/" + COURSE_TITLE, null);
483         return longTitle;
484     }
485 
486     protected VerticalSection generateDescriptionRationaleSection() {
487         SectionTitle title = getH4Title(LUUIConstants.PROPOSAL_TITLE_SECTION_LABEL_KEY);
488         VerticalSection description = initSection(title, !WITH_DIVIDER);
489         title.setStyleName("cluProposalTitleSection");
490         //FIXME [KSCOR-225] Temporary fix til we have a real rich text editor
491         //addField(description, COURSE + "/" + DESCRIPTION, null);
492         addField(description, COURSE + "/" + DESCRIPTION + "/" + RichTextInfoConstants.PLAIN, generateMessageInfo(LUUIConstants.DESCRIPTION_LABEL_KEY));
493         addField(description, "proposal/rationale", generateMessageInfo(LUUIConstants.PROPOSAL_RATIONALE_LABEL_KEY),
494                 new KSCharCount(modelDefinition.getMetadata(QueryPath.parse("proposal/rationale"))));
495 
496         return description;
497     }
498 
499     public Section generateCourseLogisticsSection(Section section) {
500     	if (section instanceof SectionView){
501     		((SectionView)section).setInstructions(getLabel(LUUIConstants.LOGISTICS_LABEL_KEY + "-instruct") + "<br><br>");
502     	}
503 
504         section.addSection(generateSchedulingSection());
505         section.addSection(generateDurationSection());
506         section.addSection(generateLearningResultsSection());
507         section.addSection(generateCourseFormatsSection());
508 
509         return section;
510     }
511 
512     protected Section generateLearningResultsSection() {
513         VerticalSection learningResults = initSection(getH3Title(LUUIConstants.LEARNING_RESULTS_LABEL_KEY), WITH_DIVIDER);
514         learningResults.setInstructions(getLabel(LUUIConstants.LEARNING_RESULTS_LABEL_KEY + "-instruct") + "<br><br><br>");
515 
516         learningResults.addSection(generateGradesAssessmentsSection());
517         learningResults.addSection(generateStudentRegistrationOptionsSection());
518         learningResults.addSection(generateFinalExamSection());
519         learningResults.addSection(generateOutcomesSection());
520 
521         return learningResults;
522     }
523 
524     protected Section generateOutcomesSection() {
525 
526         String path = COURSE + QueryPath.getPathSeparator() + CREDIT_OPTIONS;
527         QueryPath creditTypeFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CreditCourseConstants.TYPE);
528         QueryPath creditOptionFixedFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CREDIT_OPTION_FIXED_CREDITS);
529         QueryPath creditOptionMinFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CREDIT_OPTION_MIN_CREDITS);
530         QueryPath creditOptionMaxFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CREDIT_OPTION_MAX_CREDITS);
531         QueryPath creditResultValuesFullPath = QueryPath.concat(path, QueryPath.getWildCard(), "resultValues");
532 
533         VerticalSection courseOutcomes = initSection(getH3Title(LUUIConstants.LEARNING_RESULT_OUTCOME_LABEL_KEY), WITH_DIVIDER);
534         Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition =
535             new HashMap<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>>();
536         SwapCompositeCondition fixedCreditCondition = new SwapCompositeCondition(
537                 CompositeConditionOperator.AND);
538         fixedCreditCondition.getChildrenConditions().add(
539                 makeCondition(creditTypeFullPath, LUUIConstants.LEARNING_RESULT_OUTCOME_TYPE_LABEL_KEY, "kuali.resultComponentType.credit.degree.fixed")
540         );
541         fixedCreditCondition.setConditionId("1");
542         SwapCompositeCondition multipleCreditCondition = new SwapCompositeCondition(
543                 CompositeConditionOperator.AND);
544         multipleCreditCondition.getChildrenConditions().add(
545                 makeCondition(creditTypeFullPath, LUUIConstants.LEARNING_RESULT_OUTCOME_TYPE_LABEL_KEY, "kuali.resultComponentType.credit.degree.multiple")
546         );
547         multipleCreditCondition.setConditionId("2");
548         SwapCompositeCondition variableCreditCondition = new SwapCompositeCondition(
549                 CompositeConditionOperator.AND);
550         variableCreditCondition.getChildrenConditions().add(
551                 makeCondition(creditTypeFullPath, LUUIConstants.LEARNING_RESULT_OUTCOME_TYPE_LABEL_KEY, "kuali.resultComponentType.credit.degree.range")
552         );
553         variableCreditCondition.setConditionId("3");
554         
555         swappableFieldsDefinition.put(fixedCreditCondition,
556                 Arrays.asList(
557                         new SwapCompositeConditionFieldConfig(
558                                 new MultiplicityFieldConfiguration(
559                                         creditOptionFixedFullPath.toString(), 
560                                         generateMessageInfo(LUUIConstants.CREDIT_OPTION_FIXED_CREDITS_LABEL_KEY),
561                                         modelDefinition.getMetadata(creditOptionFixedFullPath),
562                                         null),
563                                 null
564                         )
565                 )
566         );
567         MultiplicityFieldWidgetInitializer multipleCreditInitializer = 
568             new MultiplicityFieldWidgetInitializer() {
569                 @Override
570                 public ModelWidgetBinding<?> getModelWidgetBindingInstance() {
571                     return new ListOfStringBinding();
572                 }
573                 @Override
574                 public Widget getNewWidget() {
575                     return new ListOfStringWidget("Add Item");
576                 }
577         };
578         
579         swappableFieldsDefinition.put(multipleCreditCondition,
580                 Arrays.asList(
581                         new SwapCompositeConditionFieldConfig(
582                                 new MultiplicityFieldConfiguration(
583                                         creditResultValuesFullPath.toString(),
584                                         generateMessageInfo(LUUIConstants.CREDIT_OPTION_FIXED_CREDITS_LABEL_KEY),
585                                         modelDefinition.getMetadata(creditResultValuesFullPath),
586                                         multipleCreditInitializer),
587                                 null
588                         )
589                 )
590         );
591         swappableFieldsDefinition.put(variableCreditCondition,
592                 Arrays.asList(
593                         new SwapCompositeConditionFieldConfig(
594                                 new MultiplicityFieldConfiguration(
595                                         creditOptionMinFullPath.toString(), 
596                                         generateMessageInfo(LUUIConstants.CREDIT_OPTION_MIN_CREDITS_LABEL_KEY),
597                                         modelDefinition.getMetadata(creditOptionMinFullPath),
598                                         null),
599                                 null
600                         ),
601                         new SwapCompositeConditionFieldConfig(
602                                 new MultiplicityFieldConfiguration(
603                                         creditOptionMaxFullPath.toString(), 
604                                         generateMessageInfo(LUUIConstants.CREDIT_OPTION_MAX_CREDITS_LABEL_KEY),
605                                         modelDefinition.getMetadata(creditOptionMaxFullPath),
606                                         null),
607                                 null
608                         )
609                 )
610         );
611         
612         MultiplicitySection ms = addMultiplicityFields(
613                 courseOutcomes, 
614                 path, 
615                 LUUIConstants.LEARNING_RESULT_OUTCOME_LABEL_KEY,
616                 LUUIConstants.LEARNING_RESULT_OUTCOME_LABEL_KEY,
617                 Arrays.asList(
618                 		new MultiplicityFieldConfig(
619                                 CreditCourseConstants.TYPE,
620                                 LUUIConstants.LEARNING_RESULT_OUTCOME_TYPE_LABEL_KEY,
621                                 null, null, true)
622                 ), swappableFieldsDefinition, null,1);
623         //Set the required panel
624         courseOutcomes.setRequired(ms.getConfig().getParentFd().getFieldElement().getRequiredPanel());
625         return courseOutcomes;
626 
627     }
628 
629     protected Section generateStudentRegistrationOptionsSection() {
630         VerticalSection studentRegistrationOptionsSection = initSection(getH3Title(LUUIConstants.LEARNING_RESULTS_STUDENT_REGISTRATION_LABEL_KEY), WITH_DIVIDER);
631 
632         addField(studentRegistrationOptionsSection, COURSE + "/" + AUDIT, generateMessageInfo(LUUIConstants.LEARNING_RESULT_AUDIT_LABEL_KEY), new KSCheckBox(getLabel(LUUIConstants.LEARNING_RESULT_AUDIT_TEXT_LABEL_KEY)));
633         addField(studentRegistrationOptionsSection, COURSE + "/" + PASS_FAIL, generateMessageInfo(LUUIConstants.LEARNING_RESULT_PASS_FAIL_LABEL_KEY), new KSCheckBox(getLabel(LUUIConstants.LEARNING_RESULT_PASS_FAIL_TEXT_LABEL_KEY)));
634 
635         return studentRegistrationOptionsSection;
636     }
637 
638     protected Section generateGradesAssessmentsSection() {
639         VerticalSection gradesAssessments = initSection(getH3Title(LUUIConstants.LEARNING_RESULTS_GRADES_ASSESSMENTS_LABEL_KEY), WITH_DIVIDER);
640 
641         addField(gradesAssessments, COURSE + "/" + GRADING_OPTIONS, generateMessageInfo(LUUIConstants.LEARNING_RESULT_ASSESSMENT_SCALE_LABEL_KEY));
642 
643         return gradesAssessments;
644     }
645 
646     protected VerticalSection generateCourseFormatsSection() {
647         //COURSE FORMATS
648         VerticalSection courseFormats = initSection(getH3Title(LUUIConstants.FORMATS_LABEL_KEY), WITH_DIVIDER);
649         courseFormats.setHelp(getLabel(LUUIConstants.FORMATS_LABEL_KEY + "-help"));
650         courseFormats.setInstructions(getLabel(LUUIConstants.FORMATS_LABEL_KEY + "-instruct"));
651         MultiplicityConfiguration courseFormatConfig = setupMultiplicityConfig(
652                 MultiplicityConfiguration.MultiplicityType.GROUP,
653                 MultiplicityConfiguration.StyleType.TOP_LEVEL_GROUP,
654                 COURSE + "/" + FORMATS, LUUIConstants.COURSE_ADD_FORMAT_LABEL_KEY,
655                 LUUIConstants.FORMAT_LABEL_KEY,
656                 null, null, null);
657         courseFormatConfig.setDefaultItemsCreated(1);
658         MultiplicityConfiguration activitiesConfig = setupMultiplicityConfig(
659                 MultiplicityConfiguration.MultiplicityType.GROUP,
660                 MultiplicityConfiguration.StyleType.SUB_LEVEL_GROUP,
661                 COURSE + "/" + FORMATS + "/*/" + ACTIVITIES, 
662                 LUUIConstants.ADD_ACTIVITY_LABEL_KEY,
663                 LUUIConstants.ACTIVITY_LITERAL_LABEL_KEY,
664                 Arrays.asList(
665                         new MultiplicityFieldConfig(
666                                 ACTIVITY_TYPE,
667                                 LUUIConstants.ACTIVITY_TYPE_LABEL_KEY,
668                                 null,
669                                 null,
670                                 true),
671                         new MultiplicityFieldConfig(
672                                 CONTACT_HOURS + "/" + "unitQuantity",
673                                 LUUIConstants.CONTACT_HOURS_LABEL_KEY,
674                                 null,
675                                 null,
676                                 false),
677                         new MultiplicityFieldConfig(
678                                 CONTACT_HOURS + "/" + "unitTypeKey",
679                                 LUUIConstants.CONTACT_HOURS_FREQUENCY_LABEL_KEY,
680                                 null,
681                                 null,
682                                 true),
683                         new MultiplicityFieldConfig(
684                                 CreditCourseActivityConstants.DURATION + "/" + "atpDurationTypeKey",
685                                 LUUIConstants.COURSE_FORMATS_DURATION_TYPE_LABEL_KEY,
686                                 null,
687                                 null,
688                                 false),
689                         new MultiplicityFieldConfig(
690                                 CreditCourseActivityConstants.DURATION + "/" + "timeQuantity",
691                                 LUUIConstants.DURATION_QUANTITY_LABEL_KEY,
692                                 null,
693                                 null,
694                                 true),
695                         new MultiplicityFieldConfig(
696                                 DEFAULT_ENROLLMENT_ESTIMATE,
697                                 LUUIConstants.CLASS_SIZE_LABEL_KEY,
698                                 null,
699                                 null,
700                                 true)
701                 ), null, null);
702         activitiesConfig.setDefaultItemsCreated(1);
703         courseFormatConfig.setNestedConfig(activitiesConfig);
704         
705 
706         MultiplicitySection ms = null;
707         ms = new MultiplicitySection(courseFormatConfig, 
708                 null, null);
709         courseFormats.addSection(ms);
710         courseFormats.setRequired(courseFormatConfig.getParentFd().getFieldElement().getRequiredPanel());
711         return courseFormats;
712     }
713 
714     protected VerticalSection generateSchedulingSection() {
715         VerticalSection scheduling = initSection(getH3Title(LUUIConstants.SCHEDULING_LABEL_KEY), WITH_DIVIDER);
716         addField(scheduling, COURSE + "/" + TERMS_OFFERED, generateMessageInfo(LUUIConstants.TERMS_OFFERED_LABEL_KEY));
717         return scheduling;
718     }
719 
720     protected VerticalSection generateDurationSection() {
721         VerticalSection duration = initSection(getH3Title(LUUIConstants.DURATION_LITERAL_LABEL_KEY), WITH_DIVIDER);
722         duration.setInstructions(getLabel(LUUIConstants.DURATION_LITERAL_LABEL_KEY + "-instruct"));
723         GroupSection duration_group = new GroupSection();
724         addField(duration_group, COURSE + "/" + CreditCourseConstants.DURATION + "/" + "atpDurationTypeKey", generateMessageInfo(LUUIConstants.DURATION_TYPE_LABEL_KEY));
725         addField(duration_group, COURSE + "/" + CreditCourseConstants.DURATION + "/" + "timeQuantity", generateMessageInfo(LUUIConstants.DURATION_QUANTITY_LABEL_KEY));
726 
727         duration.addSection(duration_group);
728         return duration;
729     }
730 
731     protected VerticalSection generateFinalExamSection() {
732         VerticalSection finalExam = initSection(getH3Title(LUUIConstants.FINAL_EXAM_LABEL_KEY), WITH_DIVIDER);
733         GroupSection finalExam_group = new GroupSection();
734         GroupSection finalExamRationale_group = new GroupSection();
735         GroupSection finalExamRationale_group2 = new GroupSection();
736 
737         FieldDescriptor field = addField(finalExam_group, COURSE + "/" + CreditCourseConstants.FINAL_EXAM, generateMessageInfo(LUUIConstants.FINAL_EXAM_STATUS_LABEL_KEY));
738 
739         if (field.isVisible()){
740 	        KSSelectItemWidgetAbstract picker = (KSSelectItemWidgetAbstract) (((KSPicker) field.getFieldWidget()).getInputWidget());
741 	        final FieldDescriptor rationaleField = addField(finalExamRationale_group, COURSE + "/" + CreditCourseConstants.FINAL_EXAM_RATIONALE, generateMessageInfo(LUUIConstants.FINAL_EXAM_RATIONALE_LABEL_KEY));
742 	        rationaleField.setIgnoreShowRequired(true);
743 	        final FieldDescriptor rationaleField2 = addField(finalExamRationale_group2, COURSE + "/" + CreditCourseConstants.FINAL_EXAM_RATIONALE, generateMessageInfo(LUUIConstants.FINAL_EXAM_RATIONALE_LABEL_KEY));
744 	        rationaleField2.setIgnoreShowRequired(true);
745 	        
746 	        // Create SwapSection.
747 	        final String altKey = "ALT";
748 	        final String noneKey = "None";
749 	        SwapSection swapSection = new SwapSection(picker);
750 	        swapSection.addSection(finalExamRationale_group, altKey);
751 	        swapSection.addSection(finalExamRationale_group2, noneKey);
752 	        swapSection.setSwapEventHandler(new SwapEventHandler() {
753                 
754                 @Override
755                 public void onShowSwappableSection(String key, Section section) {
756                     progressiveEnableAndRequireSection(true, section);
757                 }
758                 
759                 @Override
760                 public void onRemoveSwappableSection(String key, Section section) {
761                     progressiveEnableAndRequireSection(false, section);
762                 }
763             });
764 	        finalExam.addSection(finalExam_group);
765 	       
766 	        finalExam.addSection(swapSection);
767 	        return finalExam;
768         } else {
769         	return new VerticalSection();
770         }
771 
772     }
773     
774     protected void progressiveEnableAndRequireSection(boolean enableAndRequire, Section section){
775         if (section != null){
776             List<FieldDescriptor> fields = section.getFields(); 
777             if ((fields != null) && (fields.size() > 0)){
778                 BaseSection.progressiveEnableAndRequireFields(enableAndRequire, fields.toArray(new FieldDescriptor[fields.size()]));
779             }
780         }
781     }
782 
783     protected VerticalSection generateInstructorsSection() {
784         VerticalSection instructors = initSection(getH3Title(LUUIConstants.INSTRUCTOR_LABEL_KEY), WITH_DIVIDER);
785         addField(instructors, COURSE + "/" + PRIMARY_INSTRUCTOR + "/personId");
786         return instructors;
787     }
788 
789     protected SectionView generateLearningObjectivesSection() {
790         VerticalSectionView section = initSectionView(CourseSections.LEARNING_OBJECTIVES, LUUIConstants.LEARNING_OBJECTIVES_LABEL_KEY);
791         section.setInstructions(getLabel(LUUIConstants.LEARNING_OBJECTIVES_LABEL_KEY + "-instruct", QueryPath.concat(COURSE, COURSE_SPECIFIC_LOS, "*", "loInfo", "desc", "plain").toString())); 
792         section.addSection(generateLearningObjectivesNestedSection());
793         return section;
794     }
795 
796     protected VerticalSection generateLearningObjectivesNestedSection() {
797         final VerticalSection los = initSection(null, NO_DIVIDER);
798 
799         QueryPath path = QueryPath.concat(COURSE, COURSE_SPECIFIC_LOS, "*", "loInfo", "descr");
800         Metadata meta = modelDefinition.getMetadata(path);
801 
802         LOBuilder loBuilder = new LOBuilder(type, state, groupName, "kuali.loRepository.key.singleUse", COURSE_SPECIFIC_LOS, meta);
803         final FieldDescriptor fd = addField(los, CreditCourseConstants.COURSE_SPECIFIC_LOS, null,loBuilder, COURSE);
804         
805         loBuilder.addValueChangeHandler(new ValueChangeHandler<List<OutlineNode<LOPicker>>>(){
806 			@Override
807 			public void onValueChange(ValueChangeEvent<List<OutlineNode<LOPicker>>> event) {
808 				los.setIsDirty(true);
809 			}        	
810         });
811         
812         // have to do this here, because decision on binding is done in ks-core,
813         // and we obviously don't want ks-core referring to LOBuilder
814         fd.setWidgetBinding(LOBuilderBinding.INSTANCE);
815 
816         los.addStyleName("KS-LUM-Section-Divider");
817         return los;
818     }
819 
820     public class PersonList extends KSDropDown {
821         final SimpleListItems people = new SimpleListItems();
822 
823         public PersonList() {
824             final PersonList us = this;
825             final String userId = Application.getApplicationContext().getSecurityContext().getUserId();
826 
827             //FIXME: [KSCOR-225] Commented out search code to display drop down with only current user, and disable select
828             people.addItem(userId, userId);
829             us.setListItems(people);
830             us.selectItem(userId);
831             us.setBlankFirstItem(false);
832             this.setEnabled(false);
833 
834             /*
835                 SearchRpcServiceAsync searchRpcServiceAsync = GWT.create(SearchRpcService.class);
836                 SearchRequest searchRequest = new SearchRequest();
837                 searchRequest.setSearchKey("person.search.personQuickViewByGivenName");
838                 searchRequest.setSortColumn("person.resultColumn.GivenName");
839                 searchRequest.setSortDirection(SortDirection.ASC);
840                 searchRpcServiceAsync.search(searchRequest, new KSAsyncCallback<SearchResult>() {
841 
842                     @Override
843                     public void onSuccess(SearchResult result) {
844                         for (SearchResultRow r : result.getRows()) {
845                             people.addItem(r.getCells().get(0).getValue(), r.getCells().get(1).getValue());
846                         }
847                         us.setListItems(people);
848                         us.selectItem(userId);
849                     }
850 
851                     @Override
852                     public void handleFailure(Throwable caught) {
853                         Window.alert("Unable to contact the SearchService for the list of users");
854                         people.addItem(userId, userId);
855                         us.setListItems(people);
856                         us.selectItem(userId);
857                     }
858                 });
859              */
860         }
861 
862         @Override
863         public boolean isMultipleSelect() {
864             return true;
865         }
866     }
867 
868     public class ProposerPersonList extends KSLabelList {
869         public ProposerPersonList() {
870             SimpleListItems list = new SimpleListItems();
871 
872             super.setListItems(list);
873         }
874     }
875 
876     protected VerticalSection generateShortTitleSection() {
877         VerticalSection shortTitle = initSection(getH3Title(LUUIConstants.SHORT_TITLE_LABEL_KEY), WITH_DIVIDER);
878         addField(shortTitle, "cluInfo/officialIdentifier/shortName", null);
879         return shortTitle;
880     }
881 
882     protected VerticalSectionView initSectionView(Enum<?> viewEnum, String labelKey) {
883         VerticalSectionView section = new VerticalSectionView(viewEnum, getLabel(labelKey), this.getModelId());
884         section.addStyleName(LUUIConstants.STYLE_SECTION);
885         return section;
886     }
887 
888 
889     protected VerticalSection initSection(SectionTitle title, boolean withDivider) {
890         VerticalSection section;
891         if (title != null) {
892             section = new VerticalSection(title);
893         } else {
894             section = new VerticalSection();
895         }
896         section.addStyleName(LUUIConstants.STYLE_SECTION);
897         if (withDivider)
898             section.addStyleName(LUUIConstants.STYLE_SECTION_DIVIDER);
899         return section;
900     }
901 
902     @Override
903     public MessageKeyInfo generateMessageInfo(String labelKey) {
904         return new MessageKeyInfo(groupName, type, state, labelKey);
905     }
906 
907     
908     protected Section generateFinancialsSection(Section section) {
909 
910         VerticalSection justiFee = initSection(getH3Title(LUUIConstants.COURSE_FEE_TITLE), WITH_DIVIDER);
911         SpanPanel courseFeeInstruction = new SpanPanel();
912         courseFeeInstruction.setStyleName("ks-form-module-elements-instruction");
913         courseFeeInstruction.setHTML(getLabel(LUUIConstants.COURSE_FEE_TITLE + FieldLayoutComponent.INSTRUCT_MESSAGE_KEY));
914         courseFeeInstruction.setVisible(true);
915         justiFee.addWidget(courseFeeInstruction);
916         
917 //        addField(description, COURSE + "/" + PROPOSAL_DESCRIPTION + "/" + RichTextInfoConstants.PLAIN, generateMessageInfo(LUConstants.DESCRIPTION_LABEL_KEY));
918 
919         addField(justiFee, COURSE + "/" + "feeJustification" + "/" + RichTextInfoConstants.PLAIN,  generateMessageInfo(LUUIConstants.JUSTIFICATION_FEE));
920         section.addSection(justiFee);
921         Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition =
922             new HashMap<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>>();
923         
924         // condition: 
925         //    if rateType field is Variable Rate Fee
926         //    if rateType field is Fixed Rate Fee
927         //    if rateType field is Multiple Rate Fee
928         //    if rateType field is Per Credit Fee
929 //        String feesPathString = COURSE + QueryPath.getPathSeparator() + FEES;
930         QueryPath feesPath = QueryPath.concat(COURSE, FEES);
931         QueryPath rateTypeFieldPath = QueryPath.concat(feesPath.toString(), QueryPath.getWildCard(), "rateType");
932 //        fees/*/feeAmounts/currencyQuantity
933         QueryPath deletionPath = QueryPath.concat(feesPath.toString(), QueryPath.getWildCard(), "feeAmounts");
934         QueryPath singularFeeAmountFieldPath = QueryPath.concat(feesPath.toString(), QueryPath.getWildCard(), "feeAmounts", "0", "currencyQuantity"); 
935         QueryPath minFeeAmountFieldPath = QueryPath.concat(feesPath.toString(), QueryPath.getWildCard(), "feeAmounts", "0", "currencyQuantity"); 
936         QueryPath maxFeeAmountFieldPath = QueryPath.concat(feesPath.toString(), QueryPath.getWildCard(), "feeAmounts", "1", "currencyQuantity"); 
937         Metadata feeAmountFieldMeta = modelDefinition.getMetadata(singularFeeAmountFieldPath);
938         
939         SwapCompositeCondition variableRateCondition = new SwapCompositeCondition(
940                 CompositeConditionOperator.AND);
941         variableRateCondition.getChildrenConditions().add(
942                 makeCondition(rateTypeFieldPath, "Rate Type", "variableRateFee")
943         );
944         variableRateCondition.setConditionId("0");
945         
946         SwapCompositeCondition fixedRateCondition = new SwapCompositeCondition(
947                 CompositeConditionOperator.AND);
948         fixedRateCondition.getChildrenConditions().add(
949                 makeCondition(rateTypeFieldPath, "Rate Type", "fixedRateFee")
950         );
951         fixedRateCondition.setConditionId("1");
952 
953         SwapCompositeCondition perCreditRateCondition = new SwapCompositeCondition(
954                 CompositeConditionOperator.AND);
955         perCreditRateCondition.getChildrenConditions().add(
956                 makeCondition(rateTypeFieldPath, "Rate Type", "perCreditFee")
957         );
958         perCreditRateCondition.setConditionId("2");
959 
960         SwapCompositeCondition multipleRateCondition = new SwapCompositeCondition(
961                 CompositeConditionOperator.AND);
962         multipleRateCondition.getChildrenConditions().add(
963                 makeCondition(rateTypeFieldPath, "Rate Type", "multipleRateFee")
964         );
965         multipleRateCondition.setConditionId("3");
966 
967         swappableFieldsDefinition.put(variableRateCondition,
968                 Arrays.asList(
969                         new SwapCompositeConditionFieldConfig(
970                                 new MultiplicityFieldConfiguration(
971                                         minFeeAmountFieldPath.toString(), 
972                                         new MessageKeyInfo("Mininum Amount"), feeAmountFieldMeta,
973                                         null),
974                                 null
975                         ),
976                         new SwapCompositeConditionFieldConfig(
977                                 new MultiplicityFieldConfiguration(
978                                         maxFeeAmountFieldPath.toString(), 
979                                         new MessageKeyInfo("Maximum Amount"), feeAmountFieldMeta,
980                                         null),
981                                 null
982                         ))
983         );
984         
985         swappableFieldsDefinition.put(fixedRateCondition,
986                 Arrays.asList(
987                         new SwapCompositeConditionFieldConfig(
988                                 new MultiplicityFieldConfiguration(
989                                         singularFeeAmountFieldPath.toString(), 
990                                         new MessageKeyInfo("Amount"), feeAmountFieldMeta,
991                                         null), 
992                                 null))
993         );
994 
995         swappableFieldsDefinition.put(perCreditRateCondition,
996                 Arrays.asList(
997                         new SwapCompositeConditionFieldConfig(
998                                 new MultiplicityFieldConfiguration(
999                                         singularFeeAmountFieldPath.toString(), 
1000                                         new MessageKeyInfo("Amount"), feeAmountFieldMeta,
1001                                         null),
1002                                 null))
1003         );
1004         
1005         MultiplicityConfiguration multipleFeesConfig = setupMultiplicityConfig(
1006                 MultiplicityConfiguration.MultiplicityType.GROUP,
1007                 MultiplicityConfiguration.StyleType.BORDERLESS_TABLE,
1008                 COURSE + QueryPath.getPathSeparator() + FEES + QueryPath.getPathSeparator() + 
1009                     QueryPath.getWildCard() + QueryPath.getPathSeparator() + "feeAmounts",
1010                 LUUIConstants.ADD_ANOTHER_FEE,
1011                 LUUIConstants.FEE,
1012                 Arrays.asList(
1013                         new MultiplicityFieldConfig(
1014                                 "currencyQuantity", 
1015                                 "Amount", null, null, true)),
1016                 null,
1017                 null);
1018         swappableFieldsDefinition.put(multipleRateCondition,
1019                 Arrays.asList(
1020                         new SwapCompositeConditionFieldConfig(
1021                                 null, multipleFeesConfig
1022                                 ))
1023                 );
1024 
1025         addFeeMultiplicityFields(justiFee, 
1026                 COURSE + QueryPath.getPathSeparator() + FEES,
1027                 LUUIConstants.ADD_A_FEE,
1028                 LUUIConstants.FEE,
1029                 Arrays.asList(
1030                         new MultiplicityFieldConfig(
1031                                 "feeType", 
1032                                 "Fee Type", null, null, true),
1033                         new MultiplicityFieldConfig(
1034                                 "rateType", 
1035                                 "Rate Type", null, null, true)),
1036                 swappableFieldsDefinition,
1037                 Arrays.asList(
1038                         deletionPath.toString()));
1039         
1040         section.addSection(justiFee);
1041         
1042         
1043         VerticalSection financialSection = initSection(getH3Title(LUUIConstants.FINANCIAL_INFORMATION), WITH_DIVIDER);
1044         SpanPanel financialInfoInstruction = new SpanPanel();
1045         financialInfoInstruction.setStyleName("ks-form-module-elements-instruction");
1046         financialInfoInstruction.setHTML(getLabel(LUUIConstants.FINANCIAL_INFORMATION + FieldLayoutComponent.INSTRUCT_MESSAGE_KEY));
1047         financialInfoInstruction.setVisible(true);
1048         financialSection.addWidget(financialInfoInstruction);
1049         SpanPanel revenuepan = new SpanPanel();
1050         revenuepan.setStyleName("ks-multiplicity-section-label");
1051         revenuepan.setHTML("<br>Revenue");
1052         revenuepan.setVisible(true);
1053         financialSection.addWidget(revenuepan);
1054         setupRevenueSection(financialSection);
1055         SpanPanel expendpan = new SpanPanel();
1056         expendpan.setStyleName("ks-multiplicity-section-label");
1057         expendpan.setHTML("<br>Expenditures");
1058         expendpan.setVisible(true);
1059         financialSection.addWidget(expendpan);
1060         setupExpenditureSection(financialSection);
1061         section.addSection(financialSection);
1062 
1063         return section;
1064     }
1065     
1066     protected void setupRevenueSection(Section parentSection) {
1067         // TODO customize multiplicity and change "Percentage" label into LUConstants.AMOUNT
1068         QueryPath revenuePath = QueryPath.concat(COURSE, "revenues");
1069         QueryPath affiliatedOrgIdSubPath = QueryPath.concat("affiliatedOrgs", "0", "orgId");
1070         QueryPath percentageSubPath = QueryPath.concat("affiliatedOrgs", "0", "percentage");
1071         addMultiplicityFields(parentSection, 
1072                 revenuePath.toString(), 
1073                 LUUIConstants.ADD_ANOTHER_ORGANIZATION, 
1074                 LUUIConstants.REVENUE,
1075                 Arrays.asList(
1076                         new MultiplicityFieldConfig(
1077                                 affiliatedOrgIdSubPath.toString(), 
1078                                 LUUIConstants.REVENUE, null, null, true),
1079                         new MultiplicityFieldConfig(
1080                                 percentageSubPath.toString(), 
1081                                 "Percentage", null, null, true)                                
1082                 ),
1083                 null,
1084                 null,
1085                 0);
1086     }
1087     
1088     protected void setupExpenditureSection(Section parentSection) {
1089         // TODO customize multiplicity and change "Percentage" label into LUConstants.AMOUNT
1090         QueryPath expenditureAffiliatedOrgPath = QueryPath.concat(COURSE, "expenditure", "affiliatedOrgs");
1091         QueryPath affiliatedOrgIdSubPath = QueryPath.concat("orgId");
1092         QueryPath percentageSubPath = QueryPath.concat("percentage");
1093         addMultiplicityFields(parentSection, 
1094                 expenditureAffiliatedOrgPath.toString(), 
1095                 LUUIConstants.ADD_ANOTHER_ORGANIZATION, 
1096                 LUUIConstants.EXPENDITURE,
1097                 Arrays.asList(
1098                         new MultiplicityFieldConfig(
1099                                 affiliatedOrgIdSubPath.toString(), 
1100                                 LUUIConstants.EXPENDITURE, null, null, true),
1101                         new MultiplicityFieldConfig(
1102                                 percentageSubPath.toString(), 
1103                                 "Percentage", null, null, true)                                
1104                 ),
1105                 null,
1106                 null,
1107                 0);
1108     }
1109     
1110     protected SwapCondition makeCondition(QueryPath fieldPath, String messageLabelKey, 
1111             String value) {
1112         SwapCondition swapCondition = new SwapCondition();
1113         swapCondition.setFd(new FieldDescriptor(
1114                 fieldPath.toString(), 
1115                 new MessageKeyInfo(messageLabelKey),
1116                 modelDefinition.getMetadata(fieldPath)));
1117         swapCondition.setValue(value);
1118         return swapCondition;
1119     }
1120 
1121 
1122     @Override
1123     public String getCourseTitlePath() {
1124         return COURSE_TITLE_PATH;
1125     }
1126 
1127     @Override
1128     public String getProposalPath() {
1129         return PROPOSAL_PATH;
1130     }
1131 
1132     @Override
1133     public String getProposalTitlePath() {
1134         return PROPOSAL_TITLE_PATH;
1135     }
1136 
1137     @Override
1138     public Class<? extends Enum<?>> getViewsEnum() {
1139         return CourseProposalConfigurer.CourseSections.class;
1140     }
1141 
1142 
1143     @Override
1144     public String getSectionTitle(DataModel model) {
1145 
1146         StringBuffer sb = new StringBuffer();
1147         sb.append("Modify Course: ");
1148         sb.append(model.get("courseCode"));
1149         sb.append(" - ");
1150         sb.append(model.get("transcriptTitle"));
1151 
1152         return sb.toString();
1153     }
1154 
1155     @Override
1156     public String getProposalHeaderTitle(DataModel model) {
1157         StringBuffer sb = new StringBuffer();
1158         if (model.get("copyOfCourseId") != null) {
1159             sb.append("Modify Course: ");
1160             sb.append(model.get("courseCode"));
1161             sb.append(" - ");
1162             sb.append(model.get("transcriptTitle"));
1163         } else {
1164             sb.append("New Course: ");
1165             sb.append(model.get(getCourseTitlePath()));
1166         }
1167 
1168         return sb.toString();
1169     }
1170 
1171     public CourseSummaryConfigurer getSummaryConfigurer() {
1172         return summaryConfigurer;
1173     }
1174 
1175     public static class KeyListModelWigetBinding extends ModelWidgetBindingSupport<HasDataValue> {
1176         protected String key;
1177         HasDataValueBinding hasDataValueBinding = HasDataValueBinding.INSTANCE;
1178 
1179         public KeyListModelWigetBinding(String key) {
1180             this.key = key;
1181         }
1182 
1183         @Override
1184         public void setModelValue(HasDataValue widget, DataModel model, String path) {
1185             // convert from the structure path/0/<id> into path/0/<key>/<id>
1186             hasDataValueBinding.setModelValue(widget, model, path);
1187 
1188             QueryPath qPath = QueryPath.parse(path);
1189             Value value = ((KSSelectedList) widget).getValueWithTranslations();
1190 
1191             Data idsData = null;
1192             Data idsDataStruct = null;
1193 
1194             if (value != null) {
1195                 idsData = value.get();
1196             }
1197             if (idsData != null) {
1198                 for (Data.Property p : idsData) {
1199                     if (!"_runtimeData".equals(p.getKey())) {
1200                         String id = p.getValue();
1201                         // old translation path path/_runtimeData/0/id-translation
1202                         QueryPath translationPath = new QueryPath();
1203                         translationPath.add(new Data.StringKey(qPath.toString()));
1204                         translationPath.add(new Data.StringKey("_runtimeData"));
1205                         translationPath.add(new Data.IntegerKey((Integer) p.getKey()));
1206                         translationPath.add(new Data.StringKey("id-translation"));
1207 
1208                         Data idItem = new Data();
1209                         String translation = model.get(translationPath.toString());
1210                         Data idItemRuntime = new Data();
1211                         Data idItemTranslation = new Data();
1212                         idsDataStruct = (idsDataStruct == null) ? new Data() : idsDataStruct;
1213                         idItem.set(this.key, id);
1214                         // new translation path/0/_runtimeData/<key>/id-translation
1215                         idItemTranslation.set("id-translation", translation);
1216                         idItemRuntime.set(this.key, idItemTranslation);
1217                         idItem.set("_runtimeData", idItemRuntime);
1218                         idsDataStruct.add(idItem);
1219                     }
1220                 }
1221             }
1222 
1223             model.set(qPath, idsDataStruct);
1224         }
1225 
1226         @Override
1227         public void setWidgetValue(HasDataValue widget, DataModel model, String path) {
1228             DataModel middleManModel = new DataModel();
1229             if (model != null && model.getRoot() != null) {
1230                 middleManModel = new DataModel(model.getDefinition(), model.getRoot().copy());
1231             }
1232             // convert from the structure path/0/<key>/<id> into path/0/<id>
1233             QueryPath qPath = QueryPath.parse(path);
1234             Object value = null;
1235             Data idsData = null;
1236             Data newIdsData = null;
1237             Data newIdsRuntimeData = null;
1238 
1239             if (middleManModel != null) {
1240                 value = middleManModel.get(qPath);
1241             }
1242 
1243             if (value != null) {
1244                 idsData = (Data) value;
1245                 if (idsData != null) {
1246                     for (Data.Property p : idsData) {
1247                         if (!"_runtimeData".equals(p.getKey())) {
1248                             Data idItem = p.getValue();
1249                             String id = idItem.get(key);
1250                             Data runtimeData = idItem.get("_runtimeData");
1251                             // KSLAB-1790 - sometime runtimeData isn't there; no idea why
1252                             Data translationData = null != runtimeData ? ((Data) runtimeData.get(key)) : new Data();
1253                             newIdsData = (newIdsData == null) ? new Data() : newIdsData;
1254                             newIdsData.add(id);
1255                             newIdsRuntimeData = (newIdsRuntimeData == null) ? new Data() : newIdsRuntimeData;
1256                             newIdsRuntimeData.add(translationData);
1257                         }
1258                     }
1259                 }
1260             }
1261             if (newIdsData != null) {
1262                 newIdsData.set("_runtimeData", newIdsRuntimeData);
1263                 middleManModel.set(qPath, newIdsData);
1264                 hasDataValueBinding.setWidgetValue(widget, middleManModel, path);
1265             }
1266         }
1267     }
1268 
1269 
1270     public static class MultiplicityFieldConfig {
1271         protected String fieldKey;
1272         protected String labelKey;
1273         boolean nextLine;
1274         
1275         public MultiplicityFieldConfig() {
1276         }
1277         public MultiplicityFieldConfig(String fieldKey, String labelKey,
1278                 Widget fieldWidget, ModelWidgetBinding<?> modelWidgetBinding, boolean nextLine) {
1279             setFieldKey(fieldKey);
1280             setLabelKey(labelKey);
1281             setNextLine(nextLine);
1282         }
1283         public String getFieldKey() {
1284             return fieldKey;
1285         }
1286         public void setFieldKey(String fieldKey) {
1287             this.fieldKey = fieldKey;
1288         }
1289         public String getLabelKey() {
1290             return labelKey;
1291         }
1292         public void setLabelKey(String labelKey) {
1293             this.labelKey = labelKey;
1294         }
1295         public boolean isNextLine() {
1296             return nextLine;
1297         }
1298         public void setNextLine(boolean nextLine) {
1299             this.nextLine = nextLine;
1300         }
1301     }
1302 }
1303 
1304 
1305 
1306