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.ViewContext;
13  import org.kuali.student.common.ui.client.configurable.mvc.layouts.MenuSectionController;
14  import org.kuali.student.common.ui.client.configurable.mvc.sections.Section;
15  import org.kuali.student.common.ui.client.configurable.mvc.views.SectionView;
16  import org.kuali.student.common.ui.client.mvc.Callback;
17  import org.kuali.student.common.ui.client.mvc.DataModel;
18  import org.kuali.student.common.ui.client.mvc.DataModelDefinition;
19  import org.kuali.student.common.ui.client.mvc.ModelProvider;
20  import org.kuali.student.common.ui.client.mvc.ModelRequestCallback;
21  import org.kuali.student.common.ui.client.mvc.View;
22  import org.kuali.student.common.ui.client.mvc.dto.ReferenceModel;
23  import org.kuali.student.common.ui.client.mvc.history.HistoryManager;
24  import org.kuali.student.common.ui.client.service.DataSaveResult;
25  import org.kuali.student.common.ui.client.util.ExportElement;
26  import org.kuali.student.common.ui.client.util.ExportUtils;
27  import org.kuali.student.common.ui.client.widgets.KSButton;
28  import org.kuali.student.common.ui.client.widgets.KSButtonAbstract;
29  import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations;
30  import org.kuali.student.common.ui.client.widgets.dialog.ButtonMessageDialog;
31  import org.kuali.student.common.ui.client.widgets.field.layout.button.ButtonGroup;
32  import org.kuali.student.common.ui.client.widgets.field.layout.button.YesNoCancelGroup;
33  import org.kuali.student.common.ui.shared.IdAttributes;
34  import org.kuali.student.common.ui.shared.IdAttributes.IdType;
35  import org.kuali.student.core.comments.ui.client.widgets.commenttool.CommentTool;
36  import org.kuali.student.lum.common.client.helpers.RecentlyViewedHelper;
37  import org.kuali.student.lum.common.client.widgets.AppLocations;
38  import org.kuali.student.lum.program.client.events.ModelLoadedEvent;
39  import org.kuali.student.lum.program.client.events.UpdateEvent;
40  import org.kuali.student.lum.program.client.properties.ProgramProperties;
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      private 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.show();
139                         } else {
140                             okToChange.exec(true);
141                         }
142                     } else {
143                         okToChange.exec(false);
144                     }
145                 }
146             });
147         } else {
148             processBeforeShow = true;
149         }
150     }
151 
152     protected void fireUpdateEvent(final Callback<Boolean> okToChange) {
153         eventBus.fireEvent(new UpdateEvent(okToChange));
154     }
155 
156     protected void resetModel() {
157         programModel.resetRoot();
158     }
159 
160     protected void resetFieldInteractionFlag() {
161         View currentView = getCurrentView();
162         if (currentView instanceof Section) {
163             ((Section) currentView).resetFieldInteractionFlags();
164         }
165     }
166 
167     /**
168      * Initialized model of the controller.
169      */
170     private void initializeModel() {
171         setDefaultModelId(ProgramConstants.PROGRAM_MODEL_ID);
172         registerModel(ProgramConstants.PROGRAM_MODEL_ID, new ModelProvider<DataModel>() {
173             @Override
174             public void requestModel(final ModelRequestCallback<DataModel> callback) {
175                 if (programModel.getRoot() == null || programModel.getRoot().size() == 0) {
176                     loadModel(callback);
177                 } else {
178                     callback.onModelReady(programModel);
179                 }
180             }
181         });
182     }
183 
184 
185     @Override
186     public void requestModel(Class modelType, ModelRequestCallback callback) {
187         if (modelType == ReferenceModel.class) {
188             ReferenceModel referenceModel = new ReferenceModel();
189             referenceModel.setReferenceId(ProgramUtils.getProgramId(programModel));
190             referenceModel.setReferenceTypeKey(ProgramConstants.MAJOR_REFERENCE_TYPE_ID);
191             referenceModel.setReferenceType(ProgramConstants.MAJOR_LU_TYPE_ID);
192             Map<String, String> attributes = new HashMap<String, String>();
193             attributes.put("name", getStringProperty("name"));
194             referenceModel.setReferenceAttributes(attributes);
195             callback.onModelReady(referenceModel);
196         } else {
197             super.requestModel(modelType, callback);
198         }
199     }
200 
201 
202     /**
203      * Loads data model from the server.
204      *
205      * @param callback we have to invoke this callback when model is loaded or failed.
206      */
207     protected void loadModel(final ModelRequestCallback<DataModel> callback) {
208         programRemoteService.getData(getViewContext().getId(), new AbstractCallback<Data>(ProgramProperties.get().common_retrievingData()) {
209 
210             @Override
211             public void onFailure(Throwable caught) {
212                 super.onFailure(caught);
213                 callback.onRequestFail(caught);
214             }
215 
216             @Override
217             public void onSuccess(Data result) {
218                 super.onSuccess(result);
219                 programModel.setRoot(result);
220                 setHeaderTitle();
221                 callback.onModelReady(programModel);
222             }
223         });
224     }
225 
226     private void setModelData() {
227         setHeaderTitle();
228         setStatus();
229         configurer.applyPermissions();
230         //We don't want to throw ModelLoadedEvent when we just want to rollback the model
231         if (needToLoadOldModel) {
232             needToLoadOldModel = false;
233         } else {
234             String id = ProgramUtils.getProgramId(programModel);
235             if (null != id) {
236                 // add to recently viewed
237                 ViewContext docContext = new ViewContext();
238                 docContext.setId(id);
239                 docContext.setIdType(IdType.OBJECT_ID);
240                 String pgmType = getStringProperty(ProgramConstants.TYPE);
241                 docContext.setAttribute(ProgramConstants.TYPE, pgmType + '/' + ProgramSections.PROGRAM_DETAILS_VIEW);
242                 RecentlyViewedHelper.addDocument(getProgramName(),
243                         HistoryManager.appendContext(getProgramViewLocation(pgmType), docContext));
244             }
245             eventBus.fireEvent(new ModelLoadedEvent(programModel));
246             onModelLoadedEvent();
247         }
248     }
249 
250     private String getProgramViewLocation(String pgmType) {
251         if (ProgramClientConstants.MAJOR_PROGRAM.equals(pgmType)) {
252             return AppLocations.Locations.VIEW_PROGRAM.getLocation();
253         } else if (ProgramClientConstants.CORE_PROGRAM.equals(pgmType)) {
254             return AppLocations.Locations.VIEW_CORE_PROGRAM.getLocation();
255         } else if (ProgramClientConstants.CREDENTIAL_PROGRAM_TYPES.contains(pgmType)) {
256             return AppLocations.Locations.VIEW_BACC_PROGRAM.getLocation();
257         }
258         return null;
259     }
260 
261     protected void setStatus() {
262         statusLabel.setText(ProgramProperties.get().common_status(getStringProperty(ProgramConstants.STATE)));
263     }
264 
265     public String getProgramName() {
266         String name = getStringProperty(ProgramConstants.LONG_TITLE);
267         if (name == null) {
268             name = ProgramProperties.get().common_newProgram();
269         }
270         return name;
271     }
272 
273     /**
274      * Got invoked by framework before showing the view of the controller.
275      *
276      * @param onReadyCallback
277      */
278     @Override
279     public void beforeShow(final Callback<Boolean> onReadyCallback) {
280         if (programModel.getRoot() == null) {
281             loadModel(new ModelRequestCallback<DataModel>() {
282                 @Override
283                 public void onModelReady(DataModel model) {
284                     if (loadMetadataCondition()) {
285                         loadMetadata(onReadyCallback);
286                     } else {
287 
288                         onReadyCallback.exec(true);
289                     }
290                 }
291 
292                 @Override
293                 public void onRequestFail(Throwable cause) {
294                     GWT.log(cause.getMessage());
295                 }
296             });
297         } else {
298             afterMetadataLoaded(onReadyCallback);
299         }
300     }
301 
302     /**
303      * We should only load metadata if the status of model is changed.
304      *
305      * @return
306      */
307     protected boolean loadMetadataCondition() {
308         return lastLoadedStatus == null || ProgramStatus.of(programModel) != lastLoadedStatus;
309     }
310 
311     /**
312      * Loads metadata from the server.
313      *
314      * @param onReadyCallback
315      */
316     protected void loadMetadata(final Callback<Boolean> onReadyCallback) {
317         Map<String, String> idAttributes = new HashMap<String, String>();
318         ViewContext viewContext = getViewContext();
319         IdType idType = viewContext.getIdType();
320         String viewContextId = null;
321         if (idType != null) {
322             idAttributes.put(IdAttributes.ID_TYPE, idType.toString());
323             viewContextId = viewContext.getId();
324             if (idType == IdType.COPY_OF_OBJECT_ID) {
325                 viewContextId = null;
326             }
327         }
328         if (programModel.getRoot() != null) {
329             ProgramStatus programStatus = ProgramStatus.of(programModel);
330             idAttributes.put(DtoConstants.DTO_STATE, programStatus.getValue());
331             if (programStatus.getNextStatus() != null) {
332                 idAttributes.put(DtoConstants.DTO_NEXT_STATE, programStatus.getNextStatus().getValue());
333             }
334         }
335         programRemoteService.getMetadata(viewContextId, idAttributes, new AbstractCallback<Metadata>() {
336 
337             @Override
338             public void onSuccess(Metadata result) {
339                 super.onSuccess(result);
340                 DataModelDefinition def = new DataModelDefinition(result);
341                 programModel.setDefinition(def);
342                 lastLoadedStatus = ProgramStatus.of(programModel);
343                 afterMetadataLoaded(onReadyCallback);
344             }
345 
346             @Override
347             public void onFailure(Throwable caught) {
348                 super.onFailure(caught);
349                 onReadyCallback.exec(false);
350             }
351         });
352     }
353 
354     protected void configureView() {
355         addStyleName("programController");
356         configurer.setModelDefinition(programModel.getDefinition());
357         configurer.configure(this);
358         addContentWidget(statusLabel);
359         setSideBarWidget(sideBar);
360     }
361 
362     @Override
363     public void setViewContext(ViewContext viewContext) {
364         super.setViewContext(viewContext);
365         if (viewContext.getId() != null && !viewContext.getId().isEmpty()) {
366             viewContext.setPermissionType(PermissionType.OPEN);
367         } else {
368             viewContext.setPermissionType(PermissionType.INITIATE);
369         }
370     }
371 
372     /**
373      * Called when metadata is loaded.
374      *
375      * @param onReadyCallback
376      */
377     protected void afterMetadataLoaded(Callback<Boolean> onReadyCallback) {
378         if (!reloadMetadata) {
379             configureView();
380             onReadyCallback.exec(true);
381             reloadMetadata = true;
382         } else {
383             onReadyCallback.exec(true);
384             ProgramUtils.syncMetadata(configurer, programModel.getDefinition());
385         }
386         if (programModel.getRoot() != null) {
387             setModelData();
388         }
389     }
390 
391     protected void setHeaderTitle() {
392         String title = getProgramName();
393         this.setContentTitle(title);
394         this.setName(title);
395     }
396 
397     protected Widget createCommentPanel() {
398         final CommentTool commentTool = new CommentTool(ProgramSections.COMMENTS, "Comments", "kuali.comment.type.generalRemarks", "Program Comments");
399         commentTool.setController(this);
400         KSButton commentsButton = new KSButton(ProgramProperties.get().comments_button(), KSButtonAbstract.ButtonStyle.DEFAULT_ANCHOR, new ClickHandler() {
401 
402             @Override
403             public void onClick(ClickEvent event) {
404                 commentTool.show();
405             }
406         });
407         return commentsButton;
408     }
409 
410     protected void doSave() {
411     }
412     
413     
414     /**
415      * Update the state of the program and all of its statements.
416      * <p>
417      * This is only called when the state change event fires.
418      * <p>
419      * There are several types of programs (majorDiscipline, core, credential).  The
420      * state of each program changes when buttons are pressed.  For example, pressing
421      * the  activate button may change the state of the program from draft to active.
422      * <p>
423      * This method is triggered when the state changes.  It will pass the 
424      * new state to the controller servlet, which will then use it to update the state
425      * by calling the web services.
426      * <p>
427      * Note that state and status are different.
428      * <p>
429      * It is placed in ProgramController so core, credential, etc all have access it.
430      * <p>
431      * 
432      * 
433      * @param programModel a map containing data representing the program
434      * @param state the state we changed to
435      * @param callback will return true if update succeeded
436      */
437      protected void updateState(String state, final Callback<Boolean> okCallback) {
438 
439        	 programRemoteService.updateState(programModel.getRoot(), state,  new AbstractCallback<DataSaveResult>(ProgramProperties.get().common_savingData()) {
440                 @Override
441                 public void onSuccess(DataSaveResult result) {
442                     super.onSuccess(result);
443                     okCallback.exec(true);
444                     refreshModelAndView(result);
445                }
446             }); 
447     	 
448      }
449      /**
450       * This method will refresh the model and view with the data sent back from
451       * the server.
452       *
453       */
454      public void refreshModelAndView(DataSaveResult result){
455          if (result != null) {
456               programModel.setRoot(result.getValue());
457          }
458          setHeaderTitle();
459          setStatus();
460      }
461 
462     public DataModel getProgramModel() {
463         return programModel;
464     }
465 
466     public void onModelLoadedEvent() {
467     }
468 
469     protected String getStringProperty(String key) {
470         return programModel.get(key);
471     }
472 
473     protected Data getDataProperty(String key) {
474         return programModel.get(key);
475     }
476     
477     public boolean isExportButtonActive() {
478         if (this.getCurrentViewEnum() != null) {
479             if (this.getCurrentViewEnum().equals(ProgramSections.SUMMARY) 
480                     || this.getCurrentViewEnum().equals(ProgramSections.VIEW_ALL)) {
481                 return true;            
482             } else {
483                 return false;
484             }
485             
486         } else {
487             return false;
488         }
489     }
490     
491     @Override
492     public ArrayList<ExportElement> getExportElementsFromView() {
493 
494         String viewName = null;
495         String sectionTitle = null;
496         View currentView = this.getCurrentView();
497         if (currentView != null) {
498             
499             ArrayList<ExportElement> exportElements = new ArrayList<ExportElement>();
500             if (currentView != null && currentView instanceof Section) {
501                 Section currentSection = (Section) currentView;
502                 List<Section> nestedSections = currentSection.getSections();
503                 for (int i = 0; i < nestedSections.size(); i++) {
504                     ExportElement sectionExportItem = new ExportElement();
505                     ArrayList<ExportElement> subList = null;
506                     Section nestedSection = nestedSections.get(i);
507                     if (nestedSection != null && nestedSection instanceof SectionView) {
508                         SectionView nestedSectionView = (SectionView) nestedSection;
509                         viewName =  nestedSectionView.getName();
510                         sectionTitle = nestedSectionView.getTitle();
511                         sectionExportItem.setSectionName(sectionTitle + " " + i + " - " + viewName);
512                         sectionExportItem.setViewName(sectionTitle + " " + i + " - " + viewName);
513                         subList = ExportUtils.getExportElementsFromView(nestedSectionView, subList, viewName, sectionTitle);
514                         if (subList != null && subList.size()> 0) {
515                             sectionExportItem.setSubset(subList);
516                             exportElements.add(sectionExportItem);
517                         }
518                     }                    
519                 }
520             }
521             return exportElements;
522             
523         } else {
524 //            logger.warn("ExportUtils.getExportElementsFromView controller currentView is null :" + this.getClass().getName());
525         }
526         return null;
527     
528     }
529 }