1 package org.kuali.student.lum.lu.ui.course.client.requirements;
2
3 import java.util.HashMap;
4 import java.util.HashSet;
5 import java.util.LinkedHashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9
10 import org.kuali.student.r2.common.dto.RichTextInfo;
11 import org.kuali.student.r1.core.statement.dto.ReqCompFieldInfo;
12 import org.kuali.student.r1.core.statement.dto.ReqComponentInfo;
13 import org.kuali.student.r1.core.statement.dto.StatementTreeViewInfo;
14 import org.kuali.student.r1.core.statement.dto.StatementTypeInfo;
15 import org.kuali.student.common.ui.client.application.Application;
16 import org.kuali.student.common.ui.client.configurable.mvc.SectionTitle;
17 import org.kuali.student.common.ui.client.configurable.mvc.views.SectionView;
18 import org.kuali.student.common.ui.client.configurable.mvc.views.VerticalSectionView;
19 import org.kuali.student.common.ui.client.mvc.Callback;
20 import org.kuali.student.common.ui.client.mvc.Controller;
21 import org.kuali.student.common.ui.client.mvc.DataModel;
22 import org.kuali.student.common.ui.client.mvc.Model;
23 import org.kuali.student.common.ui.client.mvc.ModelRequestCallback;
24 import org.kuali.student.common.ui.client.mvc.View;
25 import org.kuali.student.common.ui.client.widgets.KSButton;
26 import org.kuali.student.common.ui.client.widgets.KSButtonAbstract;
27 import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations;
28 import org.kuali.student.common.ui.client.widgets.dialog.ConfirmationDialog;
29 import org.kuali.student.common.ui.client.widgets.field.layout.button.ActionCancelGroup;
30 import org.kuali.student.common.ui.client.widgets.field.layout.element.AbbrButton;
31 import org.kuali.student.common.ui.client.widgets.field.layout.element.LabelPanel;
32 import org.kuali.student.common.ui.client.widgets.field.layout.element.SpanPanel;
33 import org.kuali.student.common.ui.client.widgets.field.layout.element.AbbrButton.AbbrButtonType;
34 import org.kuali.student.core.statement.ui.client.widgets.rules.RulesUtil;
35 import org.kuali.student.core.statement.ui.client.widgets.rules.SubrulePreviewWidget;
36 import org.kuali.student.lum.common.client.widgets.AppLocations;
37 import org.kuali.student.lum.common.client.widgets.CluSetDetailsWidget;
38 import org.kuali.student.lum.common.client.widgets.CluSetRetriever;
39 import org.kuali.student.lum.common.client.widgets.CluSetRetrieverImpl;
40 import org.kuali.student.lum.lu.ui.course.client.configuration.AbstractCourseConfigurer;
41 import org.kuali.student.lum.lu.ui.course.client.configuration.CourseProposalConfigurer;
42 import org.kuali.student.lum.lu.ui.course.client.controllers.CourseProposalController;
43
44 import com.google.gwt.core.client.GWT;
45 import com.google.gwt.event.dom.client.ClickEvent;
46 import com.google.gwt.event.dom.client.ClickHandler;
47 import com.google.gwt.user.client.Window;
48 import com.google.gwt.user.client.ui.FlowPanel;
49 import com.google.gwt.user.client.ui.HorizontalPanel;
50 import com.google.gwt.user.client.ui.VerticalPanel;
51 import com.google.gwt.user.client.ui.Widget;
52
53 public class CourseRequirementsSummaryView extends VerticalSectionView {
54
55 private static CluSetRetriever cluSetRetriever = new CluSetRetrieverImpl();
56
57
58 protected FlowPanel layout = new FlowPanel();
59 protected ActionCancelGroup actionCancelButtons = new ActionCancelGroup(
60 ButtonEnumerations.SaveContinueCancelEnum.SAVE_CONTINUE, ButtonEnumerations.SaveContinueCancelEnum.CANCEL);
61 protected Map<String, Widget> addButtonsList = new HashMap<String, Widget>();
62
63
64 protected CourseRequirementsViewController parentController;
65 protected CourseRequirementsDataModel rules;
66 protected boolean isReadOnly;
67 protected boolean showSaveButtons;
68 private static int tempProgReqInfoID = 9999;
69 public static final String NEW_STMT_TREE_ID = "NEWSTMTTREE";
70 public static final String NEW_REQ_COMP_ID = "NEWREQCOMP";
71
72 protected Map<String, SpanPanel> perCourseRequisiteTypePanel = new LinkedHashMap<String, SpanPanel>();
73
74 protected boolean displayInitialized = false;
75
76 public CourseRequirementsSummaryView() {
77 super();
78 }
79
80 public CourseRequirementsSummaryView(final CourseRequirementsViewController parentController, Enum<?> viewEnum, String name,
81 String modelId, CourseRequirementsDataModel rulesData, boolean isReadOnly, boolean showSaveButtons) {
82 super(viewEnum, name, modelId);
83 this.parentController = parentController;
84 rules = rulesData;
85 rules.setInitialized(false);
86 this.isReadOnly = isReadOnly;
87 this.showSaveButtons = showSaveButtons;
88 if (!isReadOnly && showSaveButtons) {
89 setupSaveCancelButtons();
90 }
91 }
92
93 public void init(final CourseRequirementsViewController parentController, Enum<?> viewEnum, String name,
94 String modelId, CourseRequirementsDataModel rulesData, boolean isReadOnly, boolean showSaveButtons) {
95 super.init(viewEnum, name, modelId, true);
96 this.parentController = parentController;
97 rules = rulesData;
98 rules.setInitialized(false);
99 this.isReadOnly = isReadOnly;
100 this.showSaveButtons = showSaveButtons;
101 if (!isReadOnly && showSaveButtons) {
102 setupSaveCancelButtons();
103 }
104 }
105
106 @Override
107 public boolean isDirty() {
108 return rules.isDirty();
109 }
110
111 @Override
112 public void beforeShow(final Callback<Boolean> onReadyCallback) {
113
114
115 if (!rules.isInitialized()) {
116 rules.retrieveCourseRequirements(AbstractCourseConfigurer.COURSE_PROPOSAL_MODEL, new Callback<Boolean>() {
117 @Override
118 public void exec(Boolean result) {
119 if (result) {
120 displayRules();
121 }
122 onReadyCallback.exec(result);
123 }
124 });
125 return;
126 }
127
128
129 if (isReadOnly) {
130 displayRules();
131 onReadyCallback.exec(true);
132 return;
133 }
134
135
136 if(!displayInitialized){
137 displayRules();
138 }
139
140
141 parentController.getView(CourseRequirementsViewController.CourseRequirementsViews.MANAGE, new Callback<View>(){
142 @Override
143 public void exec(View result) {
144 CourseRequirementsManageView manageView = (CourseRequirementsManageView) result;
145
146
147 if (!manageView.isDirty() || !manageView.isUserClickedSaveButton()) {
148 rules.removeEmptyRules();
149 onReadyCallback.exec(true);
150 return;
151 }
152
153
154 ((SectionView)parentController.getCurrentView()).setIsDirty(false);
155 manageView.setUserClickedSaveButton(false);
156
157
158 StatementTreeViewInfo affectedRule = rules.updateRules(manageView.getRuleTree(), manageView.getInternalCourseReqID(), manageView.isNewRule());
159 updateRequirementWidgets(affectedRule);
160
161 onReadyCallback.exec(true);
162 }
163 });
164 }
165
166 public void revertRuleChanges() {
167 rules.revertRuleChanges();
168 displayRules();
169 }
170
171 protected void updateRequirementWidgets(StatementTreeViewInfo rule) {
172 if (rule != null) {
173 StatementTypeInfo affectedStatementTypeInfo = rules.getStmtTypeInfo(rule.getType());
174 SpanPanel reqPanel = perCourseRequisiteTypePanel.get(affectedStatementTypeInfo.getId());
175
176
177 addButtonsList.get(rule.getType()).setVisible(false);
178
179
180 if (reqPanel.getWidgetCount() == 0) {
181 SubrulePreviewWidget newRulePreviewWidget = addCourseRequisite(reqPanel, rule);
182 reqPanel.add(newRulePreviewWidget);
183 return;
184 }
185
186 for (int i = 0; i < reqPanel.getWidgetCount(); i++) {
187 SubrulePreviewWidget subrulePreviewWidget = (SubrulePreviewWidget)reqPanel.getWidget(i);
188 if (!rule.getStatements().isEmpty() || !rule.getReqComponents().isEmpty()) {
189 SubrulePreviewWidget newRulePreviewWidget = addCourseRequisite(reqPanel, rule);
190 reqPanel.insert(newRulePreviewWidget, i);
191 }
192 reqPanel.remove(subrulePreviewWidget);
193 if (rule.getStatements().isEmpty() && rule.getReqComponents().isEmpty())
194 addButtonsList.get(rule.getType()).setVisible(true);
195 break;
196 }
197 }
198 }
199
200 public void displayRules() {
201 remove(layout);
202 layout.clear();
203
204
205 if (!isReadOnly) {
206 SectionTitle pageTitle = SectionTitle.generateH2Title("Course Requisites");
207 pageTitle.addStyleName("ks-layout-header");
208 layout.add(pageTitle);
209 }
210
211
212 boolean firstSubHeader = true;
213 for (StatementTypeInfo stmtTypeInfo : rules.getStmtTypes()) {
214
215
216 if (isTopStatement(stmtTypeInfo)) {
217 SectionTitle title = SectionTitle.generateH3Title(stmtTypeInfo.getName());
218 title.addStyleName("KS-Course-Requisites-Top-Stmt-Header");
219 layout.add(title);
220 firstSubHeader = true;
221 continue;
222 }
223
224
225 SpanPanel requirementsPanel = new SpanPanel();
226 perCourseRequisiteTypePanel.put(stmtTypeInfo.getId(), requirementsPanel);
227 displayRequirementSectionForGivenType(requirementsPanel, stmtTypeInfo, firstSubHeader);
228 firstSubHeader = false;
229
230
231 for (StatementTreeViewInfo ruleInfo : rules.getCourseReqInfo(stmtTypeInfo.getId())) {
232 if (!ruleInfo.getStatements().isEmpty() || !ruleInfo.getReqComponents().isEmpty()) {
233 SubrulePreviewWidget subrulePreviewWidget = addCourseRequisite(requirementsPanel, ruleInfo);
234 requirementsPanel.add(subrulePreviewWidget);
235 }
236 }
237 }
238
239
240 if (!isReadOnly && showSaveButtons) {
241 layout.add(actionCancelButtons);
242 }
243
244 addWidget(layout);
245
246 displayInitialized = true;
247 }
248
249 protected void displayRequirementSectionForGivenType(final SpanPanel requirementsPanel,
250 final StatementTypeInfo stmtTypeInfo, boolean firstSubHeader) {
251
252
253 SectionTitle title = SectionTitle.generateH3Title(stmtTypeInfo.getName());
254 title.setStyleName((firstSubHeader ? "KS-Course-Requisites-Preview-Rule-Type-First-Header" : "KS-Course-Requisites-Preview-Rule-Type-Header"));
255 layout.add(title);
256
257 LabelPanel labelExamples = new LabelPanel(stmtTypeInfo.getDescr());
258 labelExamples.getElement().setAttribute("style", "font-weight: normal; width: 80%;");
259
260 String examplesHtml = Application.getApplicationContext().getMessage(stmtTypeInfo.getId());
261
262 if (examplesHtml != null && !examplesHtml.equals("")) {
263 AbbrButton examples = new AbbrButton(AbbrButtonType.EXAMPLES);
264 examples.setVisible(true);
265 examples.setHoverHTML(examplesHtml);
266 examples.getHoverPopup().addStyleName("ks-example-popup");
267 labelExamples.add(examples);
268 }
269
270 HorizontalPanel spacer0 = new HorizontalPanel();
271 spacer0.addStyleName("KS-Course-Requisites-Button-Spacer");
272 labelExamples.add(spacer0);
273
274 layout.add(labelExamples);
275
276
277 final String stmtId = stmtTypeInfo.getId();
278 if (!isReadOnly) {
279 KSButton addCourseReqButton = new KSButton("Add " + stmtTypeInfo.getName(), KSButtonAbstract.ButtonStyle.FORM_SMALL);
280 addCourseReqButton.addClickHandler(new ClickHandler(){
281 public void onClick(ClickEvent event) {
282
283 storeRules(false, new Callback<Boolean>() {
284 @Override
285 public void exec(Boolean courseExists) {
286 if (courseExists) {
287 final StatementTreeViewInfo newRule = new StatementTreeViewInfo();
288 newRule.setId(generateStatementTreeId());
289 newRule.setType(stmtId);
290 RichTextInfo text = new RichTextInfo();
291 text.setPlain("");
292 newRule.setDesc(text);
293 parentController.getView(
294 CourseRequirementsViewController.CourseRequirementsViews.MANAGE,
295 new Callback<View>() {
296 @Override
297 public void exec(View result) {
298 rules.addRule(newRule);
299 ((CourseRequirementsManageView) result).setRuleTree(newRule, true, rules.getInternalCourseReqID(newRule));
300 parentController.showView(CourseRequirementsViewController.CourseRequirementsViews.MANAGE);
301 }
302 });
303 }
304 }
305 });
306 };
307 });
308 if (!rules.getCourseReqInfo(stmtId).isEmpty()) {
309 boolean checkReqs = true;
310 for(StatementTreeViewInfo ruleInfo : rules.getCourseReqInfo(stmtId)) {
311 if (!ruleInfo.getStatements().isEmpty() || !ruleInfo.getReqComponents().isEmpty()) {
312 checkReqs = false;
313 break;
314 }
315 }
316 addCourseReqButton.setVisible(checkReqs);
317 } else
318 addCourseReqButton.setVisible(rules.getCourseReqInfo(stmtId).isEmpty());
319 addButtonsList.put(stmtId, addCourseReqButton);
320 layout.add(addCourseReqButton);
321 VerticalPanel spacer = new VerticalPanel();
322 spacer.addStyleName("KS-Course-Requisites-Button-Spacer");
323 layout.add(spacer);
324 }
325
326 layout.add(requirementsPanel);
327 }
328
329 protected SubrulePreviewWidget addCourseRequisite(final SpanPanel requirementsPanel,
330 final StatementTreeViewInfo rule) {
331
332 Integer internalProgReqID = rules.getInternalCourseReqID(rule);
333 String stmtTypeId = rule.getType();
334
335 final SubrulePreviewWidget rulePreviewWidget = new SubrulePreviewWidget(rule, isReadOnly, getCluSetWidgetList(rule));
336
337 addRulePreviewWidgetHandlers(requirementsPanel, rulePreviewWidget, stmtTypeId, internalProgReqID);
338 return rulePreviewWidget;
339 }
340
341 protected void addRulePreviewWidgetHandlers(final SpanPanel requirementsPanel, final SubrulePreviewWidget subRuleWidget, final String stmtTypeId, final Integer internalProgReqID) {
342
343 subRuleWidget.addEditButtonClickHandler(new ClickHandler() {
344 public void onClick(ClickEvent event) {
345 parentController.getView(CourseRequirementsViewController.CourseRequirementsViews.MANAGE, new Callback<View>(){
346 @Override
347 public void exec(View result) {
348 ((CourseRequirementsManageView) result).setRuleTree(rules.getRule(internalProgReqID), false, internalProgReqID);
349 parentController.showView(CourseRequirementsViewController.CourseRequirementsViews.MANAGE);
350 }
351 });
352 }
353 });
354
355 subRuleWidget.addDeleteButtonClickHandler(new ClickHandler() {
356 public void onClick(ClickEvent event) {
357
358 rules.markRuleAsEdited(internalProgReqID);
359
360 rules.deleteRule(internalProgReqID);
361 requirementsPanel.remove(subRuleWidget);
362 addButtonsList.get(stmtTypeId).setVisible(true);
363 }
364 });
365 }
366
367 static public boolean isTopStatement(StatementTypeInfo stmtInfo) {
368 return ((stmtInfo.getAllowedStatementTypes() != null) && !stmtInfo.getAllowedStatementTypes().isEmpty());
369 }
370
371 static public Map<String, Widget> getCluSetWidgetList(StatementTreeViewInfo rule) {
372 Map<String, Widget> widgetList = new HashMap<String, Widget>();
373 Set<String> cluSetIds = new HashSet<String>();
374 findCluSetIds(rule, cluSetIds);
375 for (String clusetId : cluSetIds) {
376 widgetList.put(clusetId, new CluSetDetailsWidget(clusetId, cluSetRetriever));
377 }
378
379 return widgetList;
380 }
381
382 static private void findCluSetIds(StatementTreeViewInfo rule, Set<String> list) {
383
384 List<StatementTreeViewInfo> statements = rule.getStatements();
385 List<ReqComponentInfo> reqComponentInfos = rule.getReqComponents();
386
387 if ((statements != null) && (statements.size() > 0)) {
388
389 for (StatementTreeViewInfo statement : statements) {
390 findCluSetIds(statement, list);
391 }
392 } else if ((reqComponentInfos != null) && (reqComponentInfos.size() > 0)) {
393
394 for (ReqComponentInfo reqComponent : reqComponentInfos) {
395 List<ReqCompFieldInfo> fieldInfos = reqComponent.getReqCompFields();
396 for (ReqCompFieldInfo fieldInfo : fieldInfos) {
397 if (RulesUtil.isCluSetWidget(fieldInfo.getType())) {
398 list.add(fieldInfo.getValue());
399 }
400 }
401 }
402 }
403 }
404
405 private void setupSaveCancelButtons() {
406 actionCancelButtons.addStyleName("KS-Course-Requisites-Save-Button");
407 actionCancelButtons.addCallback(new Callback<ButtonEnumerations.ButtonEnum>(){
408 @Override
409 public void exec(ButtonEnumerations.ButtonEnum result) {
410 if (result == ButtonEnumerations.SaveContinueCancelEnum.SAVE_CONTINUE) {
411 storeRules(true, new Callback<Boolean>() {
412 @Override
413 public void exec(Boolean result) {
414 if (result) {
415 CourseProposalController courseController = ((CourseProposalController) parentController.getParentController());
416 courseController.showNextViewOnMenu();
417
418 }
419 }
420 });
421 } else {
422 if(! ((CourseProposalController)parentController.getController()).isNew()){
423 (parentController.getController()).showView(CourseProposalConfigurer.CourseSections.SUMMARY);
424 }
425 else{
426 Application.navigate(AppLocations.Locations.CURRICULUM_MANAGEMENT.getLocation());
427 }
428 }
429 }
430 });
431 }
432
433 public void storeRules(final boolean storeRules, final Callback<Boolean> callback) {
434 parentController.requestModel(CourseRequirementsViewController.COURSE_PROPOSAL_MODEL, new ModelRequestCallback<DataModel>() {
435 @Override
436 public void onRequestFail(Throwable cause) {
437 Window.alert(cause.getMessage());
438 GWT.log("Unable to retrieve model for course requirements view", cause);
439 callback.exec(false);
440 }
441 @Override
442 public void onModelReady(DataModel model) {
443 String courseId = model.getRoot().get("id");
444 String courseState = model.getRoot().get("stateKey");
445 if (courseId == null) {
446 final ConfirmationDialog dialog = new ConfirmationDialog("Submit Course Title", "Before saving rules please submit course proposal title");
447 dialog.getConfirmButton().addClickHandler(new ClickHandler(){
448 @Override
449 public void onClick(ClickEvent event) {
450 dialog.hide();
451 }
452 });
453 dialog.show();
454 callback.exec(false);
455 } else {
456 if (storeRules) {
457 rules.updateCourseRequisites(courseId, courseState, new Callback<List<StatementTreeViewInfo>>() {
458 @Override
459 public void exec(List<StatementTreeViewInfo> rules) {
460 for (StatementTreeViewInfo rule : rules) {
461 updateRequirementWidgets(rule);
462 }
463 callback.exec(true);
464 }
465 });
466 } else {
467 callback.exec(true);
468 }
469 }
470 }
471 });
472 }
473
474 static public String generateStatementTreeId() {
475 return (NEW_STMT_TREE_ID + Integer.toString(tempProgReqInfoID++));
476 }
477 }