View Javadoc

1   package org.kuali.student.lum.program.client;
2   
3   import java.util.ArrayList;
4   import java.util.HashMap;
5   import java.util.List;
6   import java.util.Map;
7   
8   import org.kuali.student.common.assembly.data.Data;
9   import org.kuali.student.common.assembly.data.Metadata;
10  import org.kuali.student.common.dto.DtoConstants;
11  import org.kuali.student.common.rice.authorization.PermissionType;
12  import org.kuali.student.common.ui.client.application.Application;
13  import org.kuali.student.common.ui.client.application.ViewContext;
14  import org.kuali.student.common.ui.client.configurable.mvc.layouts.MenuSectionController;
15  import org.kuali.student.common.ui.client.configurable.mvc.sections.Section;
16  import org.kuali.student.common.ui.client.configurable.mvc.views.SectionView;
17  import org.kuali.student.common.ui.client.mvc.Callback;
18  import org.kuali.student.common.ui.client.mvc.DataModel;
19  import org.kuali.student.common.ui.client.mvc.DataModelDefinition;
20  import org.kuali.student.common.ui.client.mvc.ModelProvider;
21  import org.kuali.student.common.ui.client.mvc.ModelRequestCallback;
22  import org.kuali.student.common.ui.client.mvc.View;
23  import org.kuali.student.common.ui.client.mvc.dto.ReferenceModel;
24  import org.kuali.student.common.ui.client.mvc.history.HistoryManager;
25  import org.kuali.student.common.ui.client.service.DataSaveResult;
26  import org.kuali.student.common.ui.client.util.ExportElement;
27  import org.kuali.student.common.ui.client.util.ExportUtils;
28  import org.kuali.student.common.ui.client.widgets.KSButton;
29  import org.kuali.student.common.ui.client.widgets.KSButtonAbstract;
30  import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations;
31  import org.kuali.student.common.ui.client.widgets.dialog.ButtonMessageDialog;
32  import org.kuali.student.common.ui.client.widgets.field.layout.button.ButtonGroup;
33  import org.kuali.student.common.ui.client.widgets.field.layout.button.YesNoCancelGroup;
34  import org.kuali.student.common.ui.shared.IdAttributes;
35  import org.kuali.student.common.ui.shared.IdAttributes.IdType;
36  import org.kuali.student.core.comments.ui.client.widgets.commenttool.CommentTool;
37  import org.kuali.student.lum.common.client.helpers.RecentlyViewedHelper;
38  import org.kuali.student.lum.common.client.widgets.AppLocations;
39  import org.kuali.student.lum.program.client.events.ModelLoadedEvent;
40  import org.kuali.student.lum.program.client.events.UpdateEvent;
41  import org.kuali.student.lum.program.client.rpc.AbstractCallback;
42  import org.kuali.student.lum.program.client.rpc.MajorDisciplineRpcService;
43  import org.kuali.student.lum.program.client.rpc.MajorDisciplineRpcServiceAsync;
44  import org.kuali.student.lum.program.client.widgets.ProgramSideBar;
45  
46  import com.google.gwt.core.client.GWT;
47  import com.google.gwt.event.dom.client.ClickEvent;
48  import com.google.gwt.event.dom.client.ClickHandler;
49  import com.google.gwt.event.shared.HandlerManager;
50  import com.google.gwt.user.client.ui.Label;
51  import com.google.gwt.user.client.ui.Widget;
52  
53  /**
54   * @author Igor
55   */
56  public abstract class ProgramController extends MenuSectionController {
57  
58      protected MajorDisciplineRpcServiceAsync programRemoteService;
59  
60      protected boolean initialized = false;
61  
62      protected DataModel programModel;
63  
64      protected AbstractProgramConfigurer configurer;
65  
66      protected HandlerManager eventBus;
67  
68  	protected Label statusLabel = new Label();
69  
70      protected ProgramSideBar sideBar;
71  
72      private boolean needToLoadOldModel = false;
73  
74      protected ProgramStatus lastLoadedStatus;
75  
76      protected boolean reloadMetadata = false;
77  
78      protected boolean processBeforeShow = true;
79      
80      /**
81       * Constructor.
82       *
83       * @param programModel
84       */
85      public ProgramController(String name, DataModel programModel, ViewContext viewContext, HandlerManager eventBus) {
86          super();
87          programRemoteService = createProgramRemoteService();
88          this.eventBus = eventBus;
89          this.programModel = programModel;
90          setViewContext(viewContext);
91          initializeModel();
92      }
93  
94  
95      /**
96       * Create a ProgramRpcServiceAsync appropriate for this Controller
97       */
98      protected MajorDisciplineRpcServiceAsync createProgramRemoteService() {
99          return GWT.create(MajorDisciplineRpcService.class);
100     }
101 
102     @Override
103     public void beforeViewChange(Enum<?> viewChangingTo, final Callback<Boolean> okToChange) {
104         if (processBeforeShow) {
105             super.beforeViewChange(viewChangingTo, new Callback<Boolean>() {
106 
107                 @Override
108                 public void exec(Boolean result) {
109                     if (result) {
110                         if (getCurrentView() instanceof SectionView && ((SectionView) getCurrentView()).isDirty()) {
111                             ButtonGroup<ButtonEnumerations.YesNoCancelEnum> buttonGroup = new YesNoCancelGroup();
112                             final ButtonMessageDialog<ButtonEnumerations.YesNoCancelEnum> dialog = new ButtonMessageDialog<ButtonEnumerations.YesNoCancelEnum>("Warning", "You may have unsaved changes.  Save changes?", buttonGroup);
113                             buttonGroup.addCallback(new Callback<ButtonEnumerations.YesNoCancelEnum>() {
114 
115                                 @Override
116                                 public void exec(ButtonEnumerations.YesNoCancelEnum result) {
117                                     switch (result) {
118                                         case YES:
119                                             dialog.hide();
120                                             fireUpdateEvent(okToChange);
121                                             break;
122                                         case NO:
123                                             dialog.hide();
124                                             resetModel();
125                                             needToLoadOldModel = true;
126                                             resetFieldInteractionFlag();
127                                             okToChange.exec(true);
128                                             break;
129                                         case CANCEL:
130                                             okToChange.exec(false);
131                                             dialog.hide();
132                                             // Because this event fires after the history change event we need to "undo" the history events. 
133                                             HistoryManager.logHistoryChange();  
134                                             break;
135                                     }
136                                 }
137                             });
138                             dialog.addCloseLinkClickHandler(new ClickHandler() {
139                                 
140                                 @Override
141                                 public void onClick(ClickEvent event) {
142                                     okToChange.exec(false);
143                                     dialog.hide();
144                                     // Because this event fires after the history change event we need to "undo" the history events. 
145                                     HistoryManager.logHistoryChange();  
146                                 }
147                             });
148                             dialog.show();
149                         } else {
150                             okToChange.exec(true);
151                         }
152                     } else {
153                         okToChange.exec(false);
154                     }
155                 }
156             });
157         } else {
158             processBeforeShow = true;
159         }
160     }
161 
162     protected void fireUpdateEvent(final Callback<Boolean> okToChange) {
163         eventBus.fireEvent(new UpdateEvent(okToChange));
164     }
165 
166     protected void resetModel() {
167         programModel.resetRoot();
168     }
169 
170     protected void resetFieldInteractionFlag() {
171         View currentView = getCurrentView();
172         if (currentView instanceof Section) {
173             ((Section) currentView).resetFieldInteractionFlags();
174         }
175     }
176 
177     /**
178      * Initialized model of the controller.
179      */
180     private void initializeModel() {
181         setDefaultModelId(ProgramConstants.PROGRAM_MODEL_ID);
182         registerModel(ProgramConstants.PROGRAM_MODEL_ID, new ModelProvider<DataModel>() {
183             @Override
184             public void requestModel(final ModelRequestCallback<DataModel> callback) {
185                 if (programModel.getRoot() == null || programModel.getRoot().size() == 0) {
186                     loadModel(callback);
187                 } else {
188                     callback.onModelReady(programModel);
189                 }
190             }
191         });
192     }
193 
194 
195     @Override
196     public void requestModel(Class modelType, ModelRequestCallback callback) {
197         if (modelType == ReferenceModel.class) {
198             ReferenceModel referenceModel = new ReferenceModel();
199             referenceModel.setReferenceId(ProgramUtils.getProgramId(programModel));
200             referenceModel.setReferenceTypeKey(ProgramConstants.MAJOR_REFERENCE_TYPE_ID);
201             referenceModel.setReferenceType(ProgramConstants.MAJOR_LU_TYPE_ID);
202             Map<String, String> attributes = new HashMap<String, String>();
203             attributes.put("name", getStringProperty("name"));
204             referenceModel.setReferenceAttributes(attributes);
205             callback.onModelReady(referenceModel);
206         } else {
207             super.requestModel(modelType, callback);
208         }
209     }
210 
211 
212     /**
213      * Loads data model from the server.
214      *
215      * @param callback we have to invoke this callback when model is loaded or failed.
216      */
217     protected void loadModel(final ModelRequestCallback<DataModel> callback) {
218         programRemoteService.getData(getViewContext().getId(), new AbstractCallback<Data>(getLabel(ProgramMsgConstants.COMMON_RETRIEVINGDATA)) {
219 
220             @Override
221             public void onFailure(Throwable caught) {
222                 super.onFailure(caught);
223                 callback.onRequestFail(caught);
224             }
225 
226             @Override
227             public void onSuccess(Data result) {
228                 super.onSuccess(result);
229                 programModel.setRoot(result);
230                 setHeaderTitle();
231                 callback.onModelReady(programModel);
232             }
233         });
234     }
235 
236     private void setModelData() {
237         setHeaderTitle();
238         setStatus();
239         configurer.applyPermissions();
240         //We don't want to throw ModelLoadedEvent when we just want to rollback the model
241         if (needToLoadOldModel) {
242             needToLoadOldModel = false;
243         } else {
244             String id = ProgramUtils.getProgramId(programModel);
245             if (null != id) {
246                 // add to recently viewed
247                 ViewContext docContext = new ViewContext();
248                 docContext.setId(id);
249                 docContext.setIdType(IdType.OBJECT_ID);
250                 String pgmType = getStringProperty(ProgramConstants.TYPE);
251                 docContext.setAttribute(ProgramConstants.TYPE, pgmType + '/' + ProgramSections.PROGRAM_DETAILS_VIEW);
252                 RecentlyViewedHelper.addDocument(getProgramName(),
253                         HistoryManager.appendContext(getProgramViewLocation(pgmType), docContext));
254             }
255             eventBus.fireEvent(new ModelLoadedEvent(programModel));
256             onModelLoadedEvent();
257         }
258     }
259 
260     private String getProgramViewLocation(String pgmType) {
261         if (ProgramClientConstants.MAJOR_PROGRAM.equals(pgmType)) {
262             return AppLocations.Locations.VIEW_PROGRAM.getLocation();
263         } else if (ProgramClientConstants.CORE_PROGRAM.equals(pgmType)) {
264             return AppLocations.Locations.VIEW_CORE_PROGRAM.getLocation();
265         } else if (ProgramClientConstants.CREDENTIAL_PROGRAM_TYPES.contains(pgmType)) {
266             return AppLocations.Locations.VIEW_BACC_PROGRAM.getLocation();
267         }
268         return null;
269     }
270 
271     protected void setStatus() {
272         statusLabel.setText(getLabel(ProgramMsgConstants.COMMON_STATUS,getStringProperty(ProgramConstants.STATE)));
273     }
274 
275     public String getProgramName() {
276         String name = getStringProperty(ProgramConstants.LONG_TITLE);
277         if (name == null) {
278             name = getLabel(ProgramMsgConstants.COMMON_NEWPROGRAM);
279         }
280         return name;
281     }
282 
283     /**
284      * Got invoked by framework before showing the view of the controller.
285      *
286      * @param onReadyCallback
287      */
288     @Override
289     public void beforeShow(final Callback<Boolean> onReadyCallback) {
290         if (programModel.getRoot() == null) {
291             loadModel(new ModelRequestCallback<DataModel>() {
292                 @Override
293                 public void onModelReady(DataModel model) {
294                     if (loadMetadataCondition()) {
295                         loadMetadata(onReadyCallback);
296                     } else {
297 
298                         onReadyCallback.exec(true);
299                     }
300                 }
301 
302                 @Override
303                 public void onRequestFail(Throwable cause) {
304                     GWT.log(cause.getMessage());
305                 }
306             });
307         } else {
308             afterMetadataLoaded(onReadyCallback);
309         }
310     }
311 
312     /**
313      * We should only load metadata if the status of model is changed.
314      *
315      * @return
316      */
317     protected boolean loadMetadataCondition() {
318         return lastLoadedStatus == null || ProgramStatus.of(programModel) != lastLoadedStatus;
319     }
320 
321     /**
322      * Loads metadata from the server.
323      *
324      * @param onReadyCallback
325      */
326     protected void loadMetadata(final Callback<Boolean> onReadyCallback) {
327         Map<String, String> idAttributes = new HashMap<String, String>();
328         ViewContext viewContext = getViewContext();
329         IdType idType = viewContext.getIdType();
330         String viewContextId = null;
331         if (idType != null) {
332             idAttributes.put(IdAttributes.ID_TYPE, idType.toString());
333             viewContextId = viewContext.getId();
334             if (idType == IdType.COPY_OF_OBJECT_ID) {
335    
336                 viewContextId = null;
337             }
338         }
339         if (programModel.getRoot() != null) {
340             ProgramStatus programStatus = ProgramStatus.of(programModel);
341             idAttributes.put(DtoConstants.DTO_STATE, programStatus.getValue());
342             if (programStatus.getNextStatus() != null) {
343                 idAttributes.put(DtoConstants.DTO_NEXT_STATE, programStatus.getNextStatus().getValue());
344             }
345         }
346         programRemoteService.getMetadata(viewContextId, idAttributes, new AbstractCallback<Metadata>() {
347 
348             @Override
349             public void onSuccess(Metadata result) {
350                 super.onSuccess(result);
351                 DataModelDefinition def = new DataModelDefinition(result);
352                 programModel.setDefinition(def);
353                 lastLoadedStatus = ProgramStatus.of(programModel);
354                 afterMetadataLoaded(onReadyCallback);
355             }
356 
357             @Override
358             public void onFailure(Throwable caught) {
359                 super.onFailure(caught);
360                 onReadyCallback.exec(false);
361             }
362         });
363     }
364 
365     protected void configureView() {
366         addStyleName("programController");
367         configurer.setModelDefinition(programModel.getDefinition());
368         configurer.configure(this);
369         addContentWidget(statusLabel);
370         setSideBarWidget(sideBar);
371     }
372 
373     @Override
374     public void setViewContext(ViewContext viewContext) {
375         super.setViewContext(viewContext);
376         if (viewContext.getId() != null && !viewContext.getId().isEmpty()) {
377             viewContext.setPermissionType(PermissionType.OPEN);
378         } else {
379             viewContext.setPermissionType(PermissionType.INITIATE);
380         }
381     }
382 
383     /**
384      * Called when metadata is loaded.
385      *
386      * @param onReadyCallback
387      */
388     protected void afterMetadataLoaded(Callback<Boolean> onReadyCallback) {
389         if (!reloadMetadata) {
390             configureView();
391             onReadyCallback.exec(true);
392             reloadMetadata = true;
393         } else {
394             onReadyCallback.exec(true);
395             ProgramUtils.syncMetadata(configurer, programModel.getDefinition());
396         }
397         if (programModel.getRoot() != null) {
398             setModelData();
399         }
400     }
401 
402     protected void setHeaderTitle() {
403         String title = getProgramName();
404         this.setContentTitle(title);
405         this.setName(title);
406     }
407 
408     protected Widget createCommentPanel() {
409         final CommentTool commentTool = new CommentTool(ProgramSections.COMMENTS, "Comments", "kuali.comment.type.generalRemarks", "Program Comments");
410         commentTool.setController(this);
411         KSButton commentsButton = new KSButton(getLabel(ProgramMsgConstants.COMMENTS_BUTTON), KSButtonAbstract.ButtonStyle.DEFAULT_ANCHOR, new ClickHandler() {
412 
413             @Override
414             public void onClick(ClickEvent event) {
415                 commentTool.show();
416             }
417         });
418         return commentsButton;
419     }
420 
421     protected void doSave() {
422     }
423     
424     
425     /**
426      * Update the state of the program and all of its statements.
427      * <p>
428      * This is only called when the state change event fires.
429      * <p>
430      * There are several types of programs (majorDiscipline, core, credential).  The
431      * state of each program changes when buttons are pressed.  For example, pressing
432      * the  activate button may change the state of the program from draft to active.
433      * <p>
434      * This method is triggered when the state changes.  It will pass the 
435      * new state to the controller servlet, which will then use it to update the state
436      * by calling the web services.
437      * <p>
438      * Note that state and status are different.
439      * <p>
440      * It is placed in ProgramController so core, credential, etc all have access it.
441      * <p>
442      * 
443      * 
444      * @param programModel a map containing data representing the program
445      * @param state the state we changed to
446      * @param callback will return true if update succeeded
447      */
448      protected void updateState(String state, final Callback<Boolean> okCallback) {
449 
450        	 programRemoteService.updateState(programModel.getRoot(), state,  new AbstractCallback<DataSaveResult>(getLabel(ProgramMsgConstants.COMMON_SAVINGDATA)) {
451                 @Override
452                 public void onSuccess(DataSaveResult result) {
453                 	if(result.getValidationResults()==null || result.getValidationResults().isEmpty()){
454                         super.onSuccess(result);
455                         okCallback.exec(true);
456                         refreshModelAndView(result);
457                 	}else{
458                 		//Remove the blocking progress
459                 		super.onSuccess(result);
460                 		//Do proper validation error handling
461                 		isValid(result.getValidationResults(), false, true);
462                 		ProgramUtils.handleValidationErrorsForSpecializations(result.getValidationResults(), programModel);
463                 		//return false since this was not successful
464                 		okCallback.exec(false);
465                 	}
466                }
467                 
468             }); 
469     	 
470      }
471      /**
472       * This method will refresh the model and view with the data sent back from
473       * the server.
474       *
475       */
476      public void refreshModelAndView(DataSaveResult result){
477          if (result != null) {
478               programModel.setRoot(result.getValue());
479          }
480          setHeaderTitle();
481          setStatus();
482      }
483 
484     public DataModel getProgramModel() {
485         return programModel;
486     }
487 
488     public void onModelLoadedEvent() {
489     }
490 
491     protected String getStringProperty(String key) {
492         return programModel.get(key);
493     }
494 
495     protected Data getDataProperty(String key) {
496         return programModel.get(key);
497     }
498     
499     public boolean isExportButtonActive() {
500         if (this.getCurrentViewEnum() != null) {
501             if (this.getCurrentViewEnum().equals(ProgramSections.SUMMARY) 
502                     || this.getCurrentViewEnum().equals(ProgramSections.VIEW_ALL)) {
503                 return true;            
504             } else {
505                 return false;
506             }
507             
508         } else {
509             return false;
510         }
511     }
512     
513     /**
514      * 
515      * @see org.kuali.student.common.ui.client.reporting.ReportExport#getExportTemplateName()
516      */
517     @Override
518     public String getExportTemplateName() {
519         if (this.getCurrentViewEnum().equals(ProgramSections.VIEW_ALL)){
520             return "base.template";
521         }
522         return "proposal.template";
523     }
524     
525     @Override
526     public List<ExportElement> getExportElementsFromView() {
527 
528         String viewName = null;
529         String sectionTitle = null;
530         View currentView = this.getCurrentView();
531         if (currentView != null) {
532             
533             List<ExportElement> exportElements = new ArrayList<ExportElement>();
534             ExportElement heading = new ExportElement();
535             heading.setFieldLabel("");
536             heading.setFieldValue(currentView.getName());
537             exportElements.add(heading);
538             
539             if (currentView instanceof Section) {
540                 Section currentSection = (Section) currentView;
541                 List<Section> nestedSections = currentSection.getSections();
542                 for (int i = 0; i < nestedSections.size(); i++) {
543                     ExportElement sectionExportItem = new ExportElement();
544                     ArrayList<ExportElement> subList = null;
545                     Section nestedSection = nestedSections.get(i);
546                     if (nestedSection != null && nestedSection instanceof SectionView) {
547                         SectionView nestedSectionView = (SectionView) nestedSection;
548                         viewName =  nestedSectionView.getName();
549                         sectionTitle = nestedSectionView.getTitle();
550                         sectionExportItem.setSectionName(sectionTitle + " " + i + " - " + viewName);
551                         sectionExportItem.setViewName(sectionTitle + " " + i + " - " + viewName);
552                         subList = ExportUtils.getExportElementsFromView(nestedSectionView, subList, viewName, sectionTitle);
553                         if (subList != null && subList.size()> 0) {
554                             sectionExportItem.setSubset(subList);
555                             exportElements.add(sectionExportItem);
556                         }
557                     }                    
558                 }
559             }
560             return exportElements;
561             
562         } else {
563 //            logger.warn("ExportUtils.getExportElementsFromView controller currentView is null :" + this.getClass().getName());
564         }
565         return null;
566     
567     }
568 
569     protected String getLabel(String messageKey) {
570         return Application.getApplicationContext().getUILabel(ProgramMsgConstants.PROGRAM_MSG_GROUP, messageKey);
571     }
572     
573     protected String getLabel(String messageKey, String parameter) {
574         Map<String, Object> parameters = new HashMap<String, Object>();
575         parameters.put("0", parameter);
576         return Application.getApplicationContext().getUILabel(ProgramMsgConstants.PROGRAM_MSG_GROUP, messageKey, parameters);
577     }
578     
579 }