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                 	if(result.getValidationResults()==null || result.getValidationResults().isEmpty()){
443                         super.onSuccess(result);
444                         okCallback.exec(true);
445                         refreshModelAndView(result);
446                 	}else{
447                 		//Remove the blocking progress
448                 		super.onSuccess(result);
449                 		//Do proper validation error handling
450                 		isValid(result.getValidationResults(), false, true);
451                 		ProgramUtils.handleValidationErrorsForSpecializations(result.getValidationResults(), programModel);
452                 		//return false since this was not successful
453                 		okCallback.exec(false);
454                 	}
455                }
456                 
457             }); 
458     	 
459      }
460      /**
461       * This method will refresh the model and view with the data sent back from
462       * the server.
463       *
464       */
465      public void refreshModelAndView(DataSaveResult result){
466          if (result != null) {
467               programModel.setRoot(result.getValue());
468          }
469          setHeaderTitle();
470          setStatus();
471      }
472 
473     public DataModel getProgramModel() {
474         return programModel;
475     }
476 
477     public void onModelLoadedEvent() {
478     }
479 
480     protected String getStringProperty(String key) {
481         return programModel.get(key);
482     }
483 
484     protected Data getDataProperty(String key) {
485         return programModel.get(key);
486     }
487     
488     public boolean isExportButtonActive() {
489         if (this.getCurrentViewEnum() != null) {
490             if (this.getCurrentViewEnum().equals(ProgramSections.SUMMARY) 
491                     || this.getCurrentViewEnum().equals(ProgramSections.VIEW_ALL)) {
492                 return true;            
493             } else {
494                 return false;
495             }
496             
497         } else {
498             return false;
499         }
500     }
501     
502     @Override
503     public ArrayList<ExportElement> getExportElementsFromView() {
504 
505         String viewName = null;
506         String sectionTitle = null;
507         View currentView = this.getCurrentView();
508         if (currentView != null) {
509             
510             ArrayList<ExportElement> exportElements = new ArrayList<ExportElement>();
511             if (currentView != null && currentView instanceof Section) {
512                 Section currentSection = (Section) currentView;
513                 List<Section> nestedSections = currentSection.getSections();
514                 for (int i = 0; i < nestedSections.size(); i++) {
515                     ExportElement sectionExportItem = new ExportElement();
516                     ArrayList<ExportElement> subList = null;
517                     Section nestedSection = nestedSections.get(i);
518                     if (nestedSection != null && nestedSection instanceof SectionView) {
519                         SectionView nestedSectionView = (SectionView) nestedSection;
520                         viewName =  nestedSectionView.getName();
521                         sectionTitle = nestedSectionView.getTitle();
522                         sectionExportItem.setSectionName(sectionTitle + " " + i + " - " + viewName);
523                         sectionExportItem.setViewName(sectionTitle + " " + i + " - " + viewName);
524                         subList = ExportUtils.getExportElementsFromView(nestedSectionView, subList, viewName, sectionTitle);
525                         if (subList != null && subList.size()> 0) {
526                             sectionExportItem.setSubset(subList);
527                             exportElements.add(sectionExportItem);
528                         }
529                     }                    
530                 }
531             }
532             return exportElements;
533             
534         } else {
535 //            logger.warn("ExportUtils.getExportElementsFromView controller currentView is null :" + this.getClass().getName());
536         }
537         return null;
538     
539     }
540 }