View Javadoc

1   package org.kuali.student.lum.program.client.requirements;
2   
3   import com.google.gwt.core.client.GWT;
4   import com.google.gwt.event.dom.client.ClickEvent;
5   import com.google.gwt.event.dom.client.ClickHandler;
6   import com.google.gwt.event.shared.HandlerManager;
7   import com.google.gwt.user.client.Window;
8   import com.google.gwt.user.client.ui.FlowPanel;
9   import com.google.gwt.user.client.ui.Widget;
10  
11  import org.kuali.student.r1.common.assembly.data.ConstraintMetadata;
12  import org.kuali.student.r1.common.assembly.data.Data;
13  import org.kuali.student.r1.common.assembly.data.Metadata;
14  import org.kuali.student.r1.common.assembly.data.QueryPath;
15  import org.kuali.student.r2.common.dto.RichTextInfo;
16  import org.kuali.student.r1.core.statement.dto.StatementTypeInfo;
17  import org.kuali.student.r2.common.dto.ValidationResultInfo;
18  import org.kuali.student.r1.core.statement.dto.ReqCompFieldInfo;
19  import org.kuali.student.r1.core.statement.dto.ReqComponentInfo;
20  import org.kuali.student.r1.core.statement.dto.StatementOperatorTypeKey;
21  import org.kuali.student.r1.core.statement.dto.StatementTreeViewInfo;
22  import org.kuali.student.common.ui.client.application.Application;
23  import org.kuali.student.common.ui.client.application.KSAsyncCallback;
24  import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
25  import org.kuali.student.common.ui.client.configurable.mvc.SectionTitle;
26  import org.kuali.student.common.ui.client.configurable.mvc.layouts.BasicLayout;
27  import org.kuali.student.common.ui.client.configurable.mvc.sections.GroupSection;
28  import org.kuali.student.common.ui.client.configurable.mvc.views.VerticalSectionView;
29  import org.kuali.student.common.ui.client.mvc.*;
30  import org.kuali.student.common.ui.client.service.MetadataRpcService;
31  import org.kuali.student.common.ui.client.service.MetadataRpcServiceAsync;
32  import org.kuali.student.common.ui.client.widgets.KSButton;
33  import org.kuali.student.common.ui.client.widgets.KSButtonAbstract;
34  import org.kuali.student.common.ui.client.widgets.KSLabel;
35  import org.kuali.student.common.ui.client.widgets.KSLightBox;
36  import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations;
37  import org.kuali.student.common.ui.client.widgets.dialog.ConfirmationDialog;
38  import org.kuali.student.common.ui.client.widgets.field.layout.button.ActionCancelGroup;
39  import org.kuali.student.common.ui.client.widgets.field.layout.element.MessageKeyInfo;
40  import org.kuali.student.common.ui.client.widgets.field.layout.element.SpanPanel;
41  import org.kuali.student.common.ui.client.widgets.progress.BlockingTask;
42  import org.kuali.student.common.ui.client.widgets.progress.KSBlockingProgressIndicator;
43  import org.kuali.student.core.statement.ui.client.widgets.rules.RulePreviewWidget;
44  import org.kuali.student.core.statement.ui.client.widgets.rules.RulesUtil;
45  import org.kuali.student.lum.common.client.widgets.CluSetDetailsWidget;
46  import org.kuali.student.lum.common.client.widgets.CluSetRetriever;
47  import org.kuali.student.lum.common.client.widgets.CluSetRetrieverImpl;
48  import org.kuali.student.lum.program.client.ProgramConstants;
49  import org.kuali.student.lum.program.client.ProgramMsgConstants;
50  import org.kuali.student.lum.program.client.ProgramSections;
51  import org.kuali.student.lum.program.client.widgets.EditableHeader;
52  import org.kuali.student.r2.lum.program.dto.ProgramRequirementInfo;
53  
54  import java.util.*;
55  
56  public class ProgramRequirementsSummaryView extends VerticalSectionView {
57  
58      private MetadataRpcServiceAsync metadataServiceAsync = GWT.create(MetadataRpcService.class);
59      private static CluSetRetriever cluSetRetriever = new CluSetRetrieverImpl();
60  
61      //view's widgets
62      private FlowPanel layout = new FlowPanel();
63      private ActionCancelGroup actionCancelButtons = new ActionCancelGroup(ButtonEnumerations.SaveCancelEnum.SAVE, ButtonEnumerations.SaveCancelEnum.CANCEL);
64      private BasicLayout reqCompController;
65      private FlowPanel holdFieldsPanel = new FlowPanel();
66  
67      //view's data
68      private ProgramRequirementsViewController parentController;
69      private ProgramRequirementsDataModel rules;
70      private boolean isReadOnly;
71      
72      public static int tempStmtTreeID = 9999;
73      public static final String NEW_PROG_REQ_ID = "NEWPROGREQ";
74      public static final String NEW_STMT_TREE_ID = "NEWSTMTTREE";
75      public static final String NEW_REQ_COMP_ID = "NEWREQCOMP";
76  
77  
78      private enum ProgramReqDialogView {
79          VIEW
80      }
81  
82      private static final String PROG_REQ_MODEL_ID = "progReqModelId";
83      private DataModel progReqData;
84      private BlockingTask gettingMetadataTask = new BlockingTask("Loading");
85      private static Metadata dialogMetadata = null;  //simple caching
86  
87      private Map<String, SpanPanel> perProgramRequirementTypePanel = new LinkedHashMap<String, SpanPanel>();
88      private Map<String, KSLabel> perProgramRequirementTypeTotalCredits = new LinkedHashMap<String, KSLabel>();
89  
90      public ProgramRequirementsSummaryView(final ProgramRequirementsViewController parentController, HandlerManager eventBus, Enum<?> viewEnum, String name,
91                                            String modelId, boolean isReadOnly) {
92          super(viewEnum, name, modelId);
93          init(parentController, eventBus, isReadOnly);
94      }
95  
96      public ProgramRequirementsSummaryView(final ProgramRequirementsViewController parentController, HandlerManager eventBus, Enum<?> viewEnum, String name,
97                                            String modelId, boolean isReadOnly, EditableHeader header) {
98          super(viewEnum, name, modelId, header);
99          init(parentController, eventBus, isReadOnly);
100     }
101 
102     private void init(ProgramRequirementsViewController parentController, HandlerManager eventBus, boolean isReadOnly) {
103         this.parentController = parentController;
104         this.isReadOnly = isReadOnly;
105         rules = new ProgramRequirementsDataModel(eventBus);
106 
107         if (!isReadOnly) {
108             setupSaveCancelButtons();
109         }
110     }
111 
112     @Override
113     public boolean isDirty() {
114         return rules.isDirty();
115     }
116 
117     protected ProgramRequirementsDataModel getRules() {
118         return rules;
119     }
120 
121     @Override
122     public void beforeShow(final Callback<Boolean> onReadyCallback) {
123         if (!rules.isInitialized() || parentController.reloadFlag) {
124             retrieveProgramRequirements(onReadyCallback);
125             return;
126         }
127         onReadyCallback.exec(true);
128     }
129 
130     private void retrieveProgramRequirements(final Callback<Boolean> onReadyCallback) {
131     	//Added blocking progress indicator while requirements are loaded.
132     	final BlockingTask ruleBlockingTask = new BlockingTask("Retrieving requirements");
133     	KSBlockingProgressIndicator.addTask(ruleBlockingTask);
134         rules.retrieveProgramRequirements(parentController, ProgramConstants.PROGRAM_MODEL_ID, new Callback<Boolean>() {
135             @Override
136             public void exec(Boolean result) {
137                 if (result) {
138                     displayRules();
139                 }
140                 KSBlockingProgressIndicator.removeTask(ruleBlockingTask);
141                 onReadyCallback.exec(result);
142             }
143         });
144     }
145 
146     public void storeRules(final Callback<Boolean> callback) {
147         rules.updateProgramEntities(new Callback<List<ProgramRequirementInfo>>() {
148             @Override
149             public void exec(List<ProgramRequirementInfo> programReqInfos) {
150                 for (ProgramRequirementInfo programReqInfo : programReqInfos) {
151                     updateRequirementWidgets(programReqInfo);
152                 }
153                 callback.exec(true);
154             }
155         });
156     }
157 
158      public void justStoreRules(final Callback<Boolean> callback) {
159         rules.updateProgramEntities(new Callback<List<ProgramRequirementInfo>>() {
160             @Override
161             public void exec(List<ProgramRequirementInfo> programReqInfos) {
162                 callback.exec(true);
163             }
164         });
165     }
166 
167     public void revertRuleChanges() {
168         rules.revertRuleChanges();
169         displayRules();
170     }
171 
172     protected void updateRequirementWidgets(ProgramRequirementInfo programReqInfo) {
173       if (programReqInfo != null) {
174      
175             StatementTypeInfo affectedStatementTypeInfo = rules.getStmtTypeInfo(programReqInfo.getStatement().getType());
176             SpanPanel reqPanel = perProgramRequirementTypePanel.get(affectedStatementTypeInfo.getId());
177 
178             //this happens if user previously deleted requirement but didn't save when moving to another section
179             //add widget
180             if (reqPanel.getWidgetCount() == 0) {
181                 reqPanel.add(addProgramRequirement(reqPanel, programReqInfo));
182                 return;
183             }
184 
185             //replace widget with a new version
186             for (int i = 0; i < reqPanel.getWidgetCount(); i++) {
187                 RulePreviewWidget rulePreviewWidget = (RulePreviewWidget) reqPanel.getWidget(i);
188                 if (rulePreviewWidget.getInternalProgReqID().equals(rules.getInternalProgReqID(programReqInfo))) {
189                     RulePreviewWidget newRulePreviewWidget = addProgramRequirement(reqPanel, programReqInfo);
190                     reqPanel.insert(newRulePreviewWidget, i);
191                     reqPanel.remove(rulePreviewWidget);
192                     break;
193                 }
194             }
195         }
196     }
197 
198     public void displayRules() {
199 
200         remove(layout);
201         layout.clear();
202 
203         //display 'Program Requirements' page title (don't add if read only because the section itself will display the title)
204         if (!isReadOnly) {
205             SectionTitle pageTitle = SectionTitle.generateH2Title(getLabel(ProgramMsgConstants.PROGRAMREQUIREMENTS_SUMMARYVIEWPAGETITLE));
206             //pageTitle.setStyleName("KS-Program-Requirements-Section-header");  //make the header orange
207             pageTitle.addStyleName("ks-layout-header");// change the header to green
208 
209             layout.add(pageTitle);
210         }
211 
212         //iterate and display rules for each Program Requirement type e.g. Entrance Requirements, Completion Requirements
213         Boolean firstRequirement = true;
214         perProgramRequirementTypePanel.clear();  
215         for (StatementTypeInfo stmtTypeInfo : rules.getStmtTypes()) {
216 
217             //create and display one type of program requirement section
218             SpanPanel requirementsPanel = new SpanPanel();
219             perProgramRequirementTypePanel.put(stmtTypeInfo.getId(), requirementsPanel);
220             displayRequirementSectionForGivenType(requirementsPanel, stmtTypeInfo, firstRequirement);
221             updateTotalCreditPerType(stmtTypeInfo.getId());
222             firstRequirement = false;
223 
224             //now display each requirement for this Program Requirement type
225             for (ProgramRequirementInfo ruleInfo : rules.getProgReqInfo(stmtTypeInfo.getId())) {
226                 RulePreviewWidget rulePreviewWidget = addProgramRequirement(requirementsPanel, ruleInfo);
227                 requirementsPanel.add(rulePreviewWidget);
228             }
229         }
230 
231         //save and cancel buttons
232         if (!isReadOnly) {
233             layout.add(actionCancelButtons);
234         }
235 
236         addWidget(layout);
237     }
238 
239     private void displayRequirementSectionForGivenType(final SpanPanel requirementsPanel, final StatementTypeInfo stmtTypeInfo, boolean firstRequirement) {
240 
241         //display header for this Program Requirement type e.g. Entrance Requirements; make the header plural
242         SectionTitle title = SectionTitle.generateH3Title(stmtTypeInfo.getName());
243         title.setStyleName((firstRequirement ? "KS-Program-Requirements-Preview-Rule-Type-First-Header" : "KS-Program-Requirements-Preview-Rule-Type-Header"));
244         layout.add(title);
245 
246         //add Total Credits
247         KSLabel totalCredits = new KSLabel();
248         totalCredits.addStyleName("KS-Program-Requirements-Preview-Rule-Type-Credits");
249         perProgramRequirementTypeTotalCredits.put(stmtTypeInfo.getId(), totalCredits);
250         totalCredits.setVisible(false);
251         layout.add(totalCredits);
252 
253         //add rule description
254         if (!isReadOnly) {
255             KSLabel ruleTypeDesc = new KSLabel(stmtTypeInfo.getDescr());
256             ruleTypeDesc.addStyleName("KS-Program-Requirements-Preview-Rule-Type-Desc");
257             layout.add(ruleTypeDesc);
258         }
259 
260         //display "Add Rule" button if user is in 'edit' mode
261         if (!isReadOnly) {
262             String addRuleLabel = getLabel(ProgramMsgConstants.PROGRAMREQUIREMENTS_SUMMARYVIEWPAGEADDRULE, stmtTypeInfo.getName());
263             KSButton addProgramReqBtn = new KSButton(addRuleLabel, KSButtonAbstract.ButtonStyle.FORM_SMALL);
264             addProgramReqBtn.addClickHandler(new ClickHandler() {
265                 public void onClick(ClickEvent event) {
266                     showProgramRequirementDialog(requirementsPanel, stmtTypeInfo.getId(), null);
267                 }
268             });
269             layout.add(addProgramReqBtn);
270         }
271 
272         layout.add(requirementsPanel);
273     }
274 
275     private RulePreviewWidget addProgramRequirement(final SpanPanel requirementsPanel, final ProgramRequirementInfo progReqInfo) {
276 
277         Integer internalProgReqID = rules.getInternalProgReqID(progReqInfo);
278         String stmtTypeId = progReqInfo.getStatement().getType();
279 
280         int minCredits = (progReqInfo.getMinCredits() == null ? 0 : progReqInfo.getMinCredits());
281         int maxCredits = (progReqInfo.getMaxCredits() == null ? 0 : progReqInfo.getMaxCredits());
282         String plainDesc = (progReqInfo.getDescr() == null ? "" : progReqInfo.getDescr().getPlain());
283         final RulePreviewWidget rulePreviewWidget = new RulePreviewWidget(internalProgReqID, progReqInfo.getShortTitle(),
284                 getTotalCreditsString(minCredits, maxCredits),
285                 plainDesc, progReqInfo.getStatement(),
286                 isReadOnly, getCluSetWidgetList(progReqInfo.getStatement()));
287         addRulePreviewWidgetHandlers(requirementsPanel, rulePreviewWidget, stmtTypeId, internalProgReqID);
288         return rulePreviewWidget;
289     }
290 
291     private void addRulePreviewWidgetHandlers(final SpanPanel requirementsPanel, final RulePreviewWidget rulePreviewWidget, final String stmtTypeId, final Integer internalProgReqID) {
292 
293         //PROGRAM REQUIREMENT handlers
294         rulePreviewWidget.addProgReqEditButtonClickHandler(new ClickHandler() {
295             public void onClick(ClickEvent event) {
296                 showProgramRequirementDialog(requirementsPanel, stmtTypeId, internalProgReqID);
297             }
298         });
299 
300         rulePreviewWidget.addProgReqDeleteButtonClickHandler(new ClickHandler() {
301             public void onClick(ClickEvent event) {
302                 final ConfirmationDialog dialog = new ConfirmationDialog(
303                         getLabel(ProgramMsgConstants.PROGRAMREQUIREMENTS_SUMMARYVIEWPAGEDELETEREQUIREMENTDIALOGTITLE),
304                         getLabel(ProgramMsgConstants.PROGRAMREQUIREMENTS_SUMMARYVIEWPAGEDELETEREQUIREMENTDIALOGMSG));
305 
306                 dialog.getConfirmButton().addClickHandler(new ClickHandler() {
307                     @Override
308                     public void onClick(ClickEvent event) {
309                         rules.deleteRule(internalProgReqID);
310 
311                         //remove rule from display
312                         requirementsPanel.remove(rulePreviewWidget);
313                         perProgramRequirementTypeTotalCredits.get(stmtTypeId).setVisible(false);
314                         dialog.hide();
315                     }
316                 });
317                 dialog.show();
318             }
319         });
320 
321         //SUBRULE handlers
322         rulePreviewWidget.addSubRuleAddButtonClickHandler(new ClickHandler() {
323             public void onClick(ClickEvent event) {
324                 final StatementTreeViewInfo newSubRule = new StatementTreeViewInfo();
325                 newSubRule.setId(generateStatementTreeId());
326                 newSubRule.setType(stmtTypeId);
327                 RichTextInfo text = new RichTextInfo();
328                 text.setPlain("");
329                 newSubRule.setDesc(text);
330                 parentController.getView(ProgramRequirementsViewController.ProgramRequirementsViews.MANAGE, new Callback<View>() {
331 
332                     @Override
333                     public void exec(View result) {
334                         ((ProgramRequirementsManageView) result).setRuleTree(newSubRule, true, internalProgReqID);
335                         parentController.showView(ProgramRequirementsViewController.ProgramRequirementsViews.MANAGE);
336                     }
337                 });
338             }
339         });
340 
341         rulePreviewWidget.addSubRuleEditButtonClickHandler(new Callback<RulePreviewWidget.SubRuleInfo>() {
342             public void exec(final RulePreviewWidget.SubRuleInfo subRuleInfo) {
343                 parentController.getView(ProgramRequirementsViewController.ProgramRequirementsViews.MANAGE, new Callback<View>() {
344                     @Override
345                     public void exec(View result) {
346                         ((ProgramRequirementsManageView) result).setRuleTree(subRuleInfo.getSubrule(), false, subRuleInfo.getInternalProgReqID());
347                         parentController.showView(ProgramRequirementsViewController.ProgramRequirementsViews.MANAGE);
348                     }
349                 });
350             }
351         });
352 
353         rulePreviewWidget.addSubRuleDeleteCallback(new Callback<Integer>() {
354             public void exec(final Integer internalProgReqID) {
355                 //deleting subrule does not delete the requirement (rule) itself
356                 rules.markRuleAsEdited(internalProgReqID);
357             }
358         });
359     }
360 
361     static public Map<String, Widget> getCluSetWidgetList(StatementTreeViewInfo rule) {
362         Map<String, Widget> widgetList = new HashMap<String, Widget>();
363         Set<String> cluSetIds = new HashSet<String>();
364         findCluSetIds(rule, cluSetIds);
365         for (String clusetId : cluSetIds) {
366             widgetList.put(clusetId, new CluSetDetailsWidget(clusetId, cluSetRetriever));
367         }
368 
369         return widgetList;
370     }
371 
372     private static void findCluSetIds(StatementTreeViewInfo rule, Set<String> list) {
373 
374         List<StatementTreeViewInfo> statements = rule.getStatements();
375         List<ReqComponentInfo> reqComponentInfos = rule.getReqComponents();
376 
377         if ((statements != null) && (statements.size() > 0)) {
378             // retrieve all statements
379             for (StatementTreeViewInfo statement : statements) {
380                 findCluSetIds(statement, list); // inside set the children of this statementTreeViewInfo
381             }
382         } else if ((reqComponentInfos != null) && (reqComponentInfos.size() > 0)) {
383             // retrieve all req. component LEAFS
384             for (ReqComponentInfo reqComponent : reqComponentInfos) {
385                 List<ReqCompFieldInfo> fieldInfos = reqComponent.getReqCompFields();
386                 for (ReqCompFieldInfo fieldInfo : fieldInfos) {
387                     if (RulesUtil.isCluSetWidget(fieldInfo.getType())) {
388                         list.add(fieldInfo.getValue());
389                     }
390                 }
391             }
392         }
393     }
394 
395     private void showProgramRequirementDialog(final SpanPanel requirementsPanel, final String stmtTypeId, final Integer internalProgReqID) {
396 
397         boolean isAddProgReq = (internalProgReqID == null);
398 
399         String addRuleText = (isAddProgReq ? getLabel(ProgramMsgConstants.PROGRAMREQUIREMENTS_SUMMARYVIEWPAGEADDRULE, rules.getStmtTypeName(stmtTypeId)) : "Edit " + rules.getStmtTypeName(stmtTypeId));
400         final KSLightBox dialog = new KSLightBox(addRuleText);
401 
402         final ButtonEnumerations.ButtonEnum actionButton = (isAddProgReq ? ButtonEnumerations.AddCancelEnum.ADD : ButtonEnumerations.UpdateCancelEnum.UPDATE);
403         ActionCancelGroup actionCancelButtons = new ActionCancelGroup(actionButton, ButtonEnumerations.UpdateCancelEnum.CANCEL);
404 
405         actionCancelButtons.addCallback(new Callback<ButtonEnumerations.ButtonEnum>() {
406             @Override
407             public void exec(ButtonEnumerations.ButtonEnum result) {
408                 if (result == actionButton) {
409 
410                     reqCompController.updateModel();
411 
412                     //validate and retrieve fields
413                     if (progReqData.getRoot().size() > 0) {
414                         progReqData.validate(new Callback<List<ValidationResultInfo>>() {
415                             @Override
416                             public void exec(List<ValidationResultInfo> validationResults) {
417 
418                                 //do not proceed if the user input is not valid
419                                 if (!reqCompController.isValid(validationResults, true, true)) {
420                                     return;
421                                 }
422 
423                                 //retrieve entered values and set the rule info
424                                 updateProgramInfo(requirementsPanel, stmtTypeId, internalProgReqID);
425                                 dialog.hide();
426                             }
427                         });
428                     }
429                 } else {
430                     dialog.hide();
431                 }
432             }
433         });
434 
435         createAddProgramReqDialog(dialog, actionCancelButtons, internalProgReqID);
436     }
437 
438     private void createAddProgramReqDialog(final KSLightBox dialog, final ActionCancelGroup actionCancelButtons, final Integer internalProgReqID) {
439 
440         parentController.requestModel(ProgramConstants.PROGRAM_MODEL_ID, new ModelRequestCallback<DataModel>() {
441 
442             @Override
443             public void onRequestFail(Throwable cause) {
444                 Window.alert(cause.getMessage());
445                 GWT.log("Unable to retrieve program model for program summary view", cause);
446             }
447 
448             @Override
449             public void onModelReady(DataModel model) {
450 
451                 //program has to be in the database before we can save program requirements
452                 String programId = model.getRoot().get(ProgramConstants.ID);
453                 if (programId == null) {
454                     final ConfirmationDialog dialog = new ConfirmationDialog("Save Program Key Info", "Before saving rules please save program key info");
455                     dialog.getConfirmButton().addClickHandler(new ClickHandler() {
456                         @Override
457                         public void onClick(ClickEvent event) {
458                             dialog.hide();
459                         }
460                     });
461                     dialog.show();
462                     return;
463                 }
464 
465                 if (dialogMetadata == null) {
466                     KSBlockingProgressIndicator.addTask(gettingMetadataTask);
467                     metadataServiceAsync.getMetadataList("org.kuali.student.r2.lum.program.dto.ProgramRequirementInfo", "Active", new KSAsyncCallback<Metadata>() {
468                         public void handleFailure(Throwable caught) {
469                             KSBlockingProgressIndicator.removeTask(gettingMetadataTask);
470                             Window.alert(caught.getMessage());
471                             GWT.log("getMetadataList failed for ProgramRequirementInfo", caught);
472                         }
473 
474                         public void onSuccess(final Metadata metadata) {
475                             KSBlockingProgressIndicator.removeTask(gettingMetadataTask);
476                             dialogMetadata = metadata;
477                             showDialog(dialog, actionCancelButtons, metadata, internalProgReqID);
478                         }
479                     });
480                 } else {
481                     showDialog(dialog, actionCancelButtons, dialogMetadata, internalProgReqID);
482                 }
483 
484             }
485         });
486     }
487 
488     private static FieldDescriptor createInputField(final String id, final Metadata metadata, final Map<String, Metadata> fieldDefinitionMetadata, final Map<String, FieldDescriptor> fields) {
489         FieldDescriptor fd = new FieldDescriptor(id, new MessageKeyInfo("program", "program", "draft", id), metadata);
490         fields.put(id, fd);
491 
492         //add field to the data model metadata
493         fieldDefinitionMetadata.put(id, metadata);
494 
495         return fd;
496     }
497 
498     //TODO rework to use Configurer if possible
499     private void showDialog(final KSLightBox dialog, final ActionCancelGroup actionCancelButtons, Metadata metadata, Integer internalProgReqID) {
500 
501         Map<String, Metadata> fieldDefinitionMetadata = new HashMap<String, Metadata>();
502         Map<String, FieldDescriptor> fields = new HashMap<String, FieldDescriptor>();
503         VerticalSectionView dialogPanel = new VerticalSectionView(ProgramReqDialogView.VIEW, "", PROG_REQ_MODEL_ID, false);
504         holdFieldsPanel.clear();
505 
506         dialogPanel.addField(createInputField("shortTitle", metadata.getProperties().get("shortTitle"), fieldDefinitionMetadata, fields));
507         FieldDescriptor minFd = createInputField("minCredits", metadata.getProperties().get("minCredits"), fieldDefinitionMetadata, fields);
508         FieldDescriptor maxFd = createInputField("maxCredits", metadata.getProperties().get("maxCredits"), fieldDefinitionMetadata, fields);
509         GroupSection credits = new GroupSection();
510         credits.addField(minFd);
511         credits.addField(maxFd);
512         dialogPanel.addSection(credits);
513         final Metadata descrMetadata = metadata.getProperties().get("descr").getProperties().get("plain");
514         dialogPanel.addField(createInputField("descr", descrMetadata, fieldDefinitionMetadata, fields));
515 
516         List<ConstraintMetadata> constraints = descrMetadata.getConstraints();
517         dialogPanel.addWidget(new KSLabel(constraints.get(0).getMaxLength() + getUILabel("descrLimit")));
518 
519         //setup data model
520         Metadata modelDefinitionMetadata = new Metadata();
521         modelDefinitionMetadata.setCanView(true);
522         modelDefinitionMetadata.setDataType(Data.DataType.DATA);
523         modelDefinitionMetadata.setProperties(fieldDefinitionMetadata);
524         progReqData = new DataModel();
525         progReqData.setRoot(new Data());
526         progReqData.setDefinition(new DataModelDefinition(modelDefinitionMetadata));
527 
528         //initialize fields with values if user is editing an existing rule
529         if (internalProgReqID != null) {
530             ProgramRequirementInfo progReq = rules.getProgReqByInternalId(internalProgReqID);
531             progReqData.set(QueryPath.parse("shortTitle"), progReq.getShortTitle());
532             progReqData.set(QueryPath.parse("minCredits"), progReq.getMinCredits());
533             progReqData.set(QueryPath.parse("maxCredits"), progReq.getMaxCredits());
534             progReqData.set(QueryPath.parse("descr"), (progReq.getDescr() == null ? "" : progReq.getDescr().getPlain()));
535         }
536 
537         //setup controller
538         reqCompController = new BasicLayout(null);
539         reqCompController.addView(dialogPanel);
540         reqCompController.setDefaultModelId(PROG_REQ_MODEL_ID);
541         reqCompController.registerModel(PROG_REQ_MODEL_ID, new ModelProvider<DataModel>() {
542             @Override
543             public void requestModel(final ModelRequestCallback<DataModel> callback) {
544                 callback.onModelReady(progReqData);
545             }
546         });
547 
548         //show fields
549         holdFieldsPanel.add(reqCompController);
550         reqCompController.showView(ProgramReqDialogView.VIEW);
551 
552         //layout.addStyleName("KS-Advanced-Search-Window");
553         holdFieldsPanel.add(actionCancelButtons);
554         dialog.setSize(550, 530);
555         dialog.setWidget(holdFieldsPanel);
556         dialog.show();
557     }
558 
559     private String getUILabel(final String id) {
560         return Application.getApplicationContext().getUILabel("program", null, null, id);
561     }
562 
563     private void updateProgramInfo(SpanPanel requirementsPanel, String stmtTypeId, Integer internalProgReqID) {
564 
565         ProgramRequirementInfo progReqInfo;
566         if (internalProgReqID == null) {
567             progReqInfo = new ProgramRequirementInfo();
568             progReqInfo.setId(NEW_PROG_REQ_ID + Integer.toString(tempStmtTreeID++));   //set unique id
569             progReqInfo.setTypeKey("kuali.lu.type.Requirement");
570 
571             //create a top level statement tree
572             StatementTreeViewInfo stmtTree = new StatementTreeViewInfo();
573             stmtTree.setId(generateStatementTreeId());
574             stmtTree.setType(stmtTypeId);
575             RichTextInfo text2 = new RichTextInfo();
576             text2.setPlain("");
577             stmtTree.setDesc(text2);
578             stmtTree.setOperator(StatementOperatorTypeKey.AND); //AND is top level operator for rules within a Program Requirement
579 
580             //add new statement to the rule because even if user cancel on rule manage screen, we want to have at least one statement present
581             progReqInfo.setStatement(stmtTree);
582         } else {
583             progReqInfo = rules.getProgReqByInternalId(internalProgReqID);
584         }
585 
586         RichTextInfo text = new RichTextInfo();
587         text.setPlain((String) (progReqData.getRoot().get("descr")));
588         progReqInfo.setDescr(text);
589         progReqInfo.setShortTitle((String) progReqData.getRoot().get("shortTitle"));
590         progReqInfo.setLongTitle((String) progReqData.getRoot().get("shortTitle"));
591         progReqInfo.setMinCredits((Integer) progReqData.getRoot().get("minCredits"));
592         progReqInfo.setMaxCredits((Integer) progReqData.getRoot().get("maxCredits"));
593 
594         if (internalProgReqID == null) {
595             rules.addRule(progReqInfo);
596             RulePreviewWidget rulePreviewWidget = addProgramRequirement(requirementsPanel, progReqInfo);
597             requirementsPanel.add(rulePreviewWidget);
598         } else {
599             rules.updateRule(internalProgReqID, progReqInfo);
600             for (Object aRequirementsPanel : requirementsPanel) {
601                 RulePreviewWidget rulePreviewWidget = (RulePreviewWidget) aRequirementsPanel;
602                 if (rulePreviewWidget.getInternalProgReqID().equals(internalProgReqID)) {
603                     rulePreviewWidget.updateProgInfoFields(progReqInfo.getShortTitle(),
604                             getTotalCreditsString(progReqInfo.getMinCredits(), progReqInfo.getMaxCredits()),
605                             progReqInfo.getDescr().getPlain());
606                 }
607             }
608         }
609 
610         updateTotalCreditPerType(stmtTypeId);
611     }
612 
613     private String getTotalCreditsString(int min, int max) {
614         return "Expected Total Credits:" + min + "-" + max;
615     }
616 
617     private void updateTotalCreditPerType(String stmtTypeId) {
618         int min = 0;
619         int max = 0;
620         for (ProgramRequirementInfo ruleInfo : rules.getProgReqInfo(stmtTypeId)) {
621             min += ruleInfo.getMinCredits();
622             max += ruleInfo.getMaxCredits();
623         }
624 
625         if (min != 0 || max != 0) {
626             //update total
627             perProgramRequirementTypeTotalCredits.get(stmtTypeId).setVisible(true);
628             perProgramRequirementTypeTotalCredits.get(stmtTypeId).setText(getTotalCreditsString(min, max));
629         }
630     }
631 
632     private void setupSaveCancelButtons() {
633         actionCancelButtons.addStyleName("KS-Program-Requirements-Save-Button");
634         actionCancelButtons.addCallback(new Callback<ButtonEnumerations.ButtonEnum>() {
635             @Override
636             public void exec(ButtonEnumerations.ButtonEnum result) {
637                 if (result == ButtonEnumerations.SaveCancelEnum.SAVE) {
638                     storeRules(Controller.NO_OP_CALLBACK);
639                 } else {
640                     //does not work any more: HistoryManager.navigate(AppLocations.Locations.VIEW_PROGRAM.getLocation(), parentController.getViewContext());
641                     parentController.getParentController().showView(ProgramSections.SUMMARY);
642                 }
643             }
644         });
645     }
646 
647     static public String generateStatementTreeId() {
648         return (NEW_STMT_TREE_ID + Integer.toString(tempStmtTreeID++));
649     }
650     
651     protected String getLabel(String messageKey) {
652         return Application.getApplicationContext().getUILabel(ProgramMsgConstants.PROGRAM_MSG_GROUP, messageKey);
653     }
654     
655     protected String getLabel(String messageKey, String parameter) {
656         Map<String, Object> parameters = new HashMap<String, Object>();
657         parameters.put("0", parameter);
658         return Application.getApplicationContext().getUILabel(ProgramMsgConstants.PROGRAM_MSG_GROUP, messageKey, parameters);
659     }
660 }