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