View Javadoc
1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   */
15  
16  package org.kuali.student.lum.lu.ui.course.client.controllers;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.kuali.student.common.ui.client.application.Application;
22  import org.kuali.student.common.ui.client.application.KSAsyncCallback;
23  import org.kuali.student.common.ui.client.application.ViewContext;
24  import org.kuali.student.common.ui.client.configurable.mvc.layouts.DocumentLayoutController;
25  import org.kuali.student.common.ui.client.configurable.mvc.layouts.TabMenuController;
26  import org.kuali.student.common.ui.client.mvc.Callback;
27  import org.kuali.student.common.ui.client.mvc.DataModel;
28  import org.kuali.student.common.ui.client.mvc.DataModelDefinition;
29  import org.kuali.student.common.ui.client.mvc.ModelProvider;
30  import org.kuali.student.common.ui.client.mvc.ModelRequestCallback;
31  import org.kuali.student.common.ui.client.mvc.WorkQueue;
32  import org.kuali.student.common.ui.client.mvc.WorkQueue.WorkItem;
33  import org.kuali.student.common.ui.client.mvc.dto.ReferenceModel;
34  import org.kuali.student.common.ui.client.security.AuthorizationCallback;
35  import org.kuali.student.common.ui.client.security.RequiresAuthorization;
36  import org.kuali.student.common.ui.client.util.ExportElement;
37  import org.kuali.student.common.ui.client.util.ExportUtils;
38  import org.kuali.student.common.ui.client.util.WindowTitleUtils;
39  import org.kuali.student.common.ui.client.widgets.KSButton;
40  import org.kuali.student.common.ui.client.widgets.KSButtonAbstract.ButtonStyle;
41  import org.kuali.student.common.ui.client.widgets.KSLabel;
42  import org.kuali.student.common.ui.client.widgets.KSLightBox;
43  import org.kuali.student.common.ui.client.widgets.notification.KSNotification;
44  import org.kuali.student.common.ui.client.widgets.notification.KSNotifier;
45  import org.kuali.student.common.ui.client.widgets.progress.BlockingTask;
46  import org.kuali.student.common.ui.client.widgets.progress.KSBlockingProgressIndicator;
47  import org.kuali.student.common.ui.shared.IdAttributes.IdType;
48  import org.kuali.student.lum.common.client.helpers.RecentlyViewedHelper;
49  import org.kuali.student.lum.common.client.lu.LUUIPermissions;
50  import org.kuali.student.lum.lu.ui.course.client.configuration.CourseProposalConfigurer;
51  import org.kuali.student.lum.lu.ui.course.client.configuration.ViewCourseConfigurer;
52  import org.kuali.student.lum.lu.ui.course.client.configuration.ViewCourseConfigurer.ViewCourseSections;
53  import org.kuali.student.lum.lu.ui.course.client.requirements.CourseRequirementsDataModel;
54  import org.kuali.student.lum.lu.ui.course.client.requirements.HasRequirements;
55  import org.kuali.student.lum.lu.ui.course.client.service.CourseRpcService;
56  import org.kuali.student.lum.lu.ui.course.client.service.CourseRpcServiceAsync;
57  import org.kuali.student.lum.lu.ui.course.client.widgets.CourseWorkflowActionList;
58  import org.kuali.student.r1.common.assembly.data.Data;
59  import org.kuali.student.r1.common.assembly.data.Metadata;
60  import org.kuali.student.r1.common.rice.authorization.PermissionTypeGwt;
61  import org.kuali.student.r1.core.statement.dto.StatementTypeInfo;
62  import org.kuali.student.r2.common.dto.DtoConstants;
63  
64  import com.google.gwt.core.client.GWT;
65  import com.google.gwt.event.dom.client.ClickEvent;
66  import com.google.gwt.event.dom.client.ClickHandler;
67  import com.google.gwt.event.logical.shared.CloseEvent;
68  import com.google.gwt.event.logical.shared.CloseHandler;
69  import com.google.gwt.user.client.Window;
70  import com.google.gwt.user.client.ui.Widget;
71  
72  /**
73   * This is a description of what this class does - Will Gomes don't forget to fill this in. 
74   * 
75   * @author Kuali Student Team
76   *
77   */
78  public class ViewCourseController extends TabMenuController implements DocumentLayoutController, HasRequirements, RequiresAuthorization {
79      protected final DataModel cluModel = new DataModel(); 
80     
81      private WorkQueue modelRequestQueue;
82  
83      private String cluType = "kuali.lu.type.CreditCourse";
84      private String courseId = null;
85      
86      private static final String CLU_STATE = DtoConstants.STATE_ACTIVE;
87      private static final String MSG_GROUP = "course";
88      
89      private final String REFERENCE_TYPE = "referenceType.clu";
90      private boolean initialized = false;
91      CourseRpcServiceAsync rpcServiceAsync = GWT.create(CourseRpcService.class);
92      
93  	private final BlockingTask loadDataTask = new BlockingTask("Retrieving Data....");
94  	private final BlockingTask initTask = new BlockingTask("Initializing....");
95  	private final KSLabel statusLabel = new KSLabel("");
96  	
97  	private final List<CourseWorkflowActionList> actionDropDownWidgets = new ArrayList<CourseWorkflowActionList>();
98  
99      private CourseRequirementsDataModel reqDataModel;
100     
101     final ViewCourseConfigurer cfg = GWT.create(ViewCourseConfigurer.class);
102     
103     public ViewCourseController(){
104         super(CourseProposalController.class.getName());
105     }
106 	            
107     @Override
108     public void setViewContext(ViewContext viewContext) {
109     	super.setViewContext(viewContext);
110     	if(viewContext.getId() != null && !viewContext.getId().isEmpty()){
111     		viewContext.setPermissionTypeGwt(PermissionTypeGwt.OPEN);
112     		this.setCourseId(viewContext.getId());
113     	}
114     }
115     
116     public void initialize(Enum<?> viewType) {
117         addStyleName("courseView");
118         reqDataModel = new CourseRequirementsDataModel(this);
119         this.tabPanel.addStyleName("standard-content-padding");
120         this.setViewEnum(viewType);
121         super.setDefaultModelId(CourseProposalConfigurer.COURSE_PROPOSAL_MODEL);
122         super.registerModel(CourseProposalConfigurer.COURSE_PROPOSAL_MODEL, new ModelProvider<DataModel>() {
123 
124             @Override
125             public void requestModel(final ModelRequestCallback<DataModel> callback) {
126                 if (modelRequestQueue == null){
127                     modelRequestQueue = new WorkQueue();
128                 }
129 
130                 WorkItem workItem = new WorkItem(){
131                     @Override
132                     public void exec(Callback<Boolean> workCompleteCallback) {
133                         if (cluModel.getRoot() == null || cluModel.getRoot().size() == 0){
134                             if (courseId != null){
135                                 getCourseFromCluId(callback, workCompleteCallback);
136                             } else{
137                                 createNewCluModel(callback, workCompleteCallback);
138                             }                
139                         } else {
140                             callback.onModelReady(cluModel);
141                             workCompleteCallback.exec(true);
142                         }
143                     }               
144                 };
145                 modelRequestQueue.submit(workItem);
146                 
147             }
148             
149         });
150     }
151     
152      
153     public Widget generateActionDropDown(){		    	
154     	CourseWorkflowActionList actionList = GWT.create(CourseWorkflowActionList.class);
155     	actionList.initialise(this.getMessage("cluActionsLabel"), getViewContext(), "/HOME/CURRICULUM_HOME/COURSE_PROPOSAL", cluModel, new Callback<String>() {
156 
157     		@Override
158     		public void exec(String newState) {
159     			if (newState != null) {
160                     KSNotifier.add(new KSNotification(getMessage("cluStateChangeNotification" + newState), false, 5000));
161                     // FIXME: this is not updating the cluModel so state will not be updated in the model.  May not be a problem.
162                             statusLabel.setText("Course Status: " + newState);
163     			} else {
164                     KSNotifier.add(new KSNotification(getMessage("cluStateChangeFailedNotification"), false, 5000));
165     			}
166     		}
167     	});
168         actionDropDownWidgets.add(actionList);
169         
170     	return actionList;
171     }
172    
173     private void init(final Callback<Boolean> onReadyCallback) {
174 
175         if (initialized) {
176             onReadyCallback.exec(true);
177         } else {
178         	KSBlockingProgressIndicator.addTask(initTask);
179         	this.setContentTitle("View Course");
180     		this.setName("Course");
181     		String idType = null;
182     		String viewContextId = null;
183     		// The switch was added due to the way permissions currently work.
184     		// For a new Create Course Proposal or Modify Course we send nulls so that permissions are not checked.
185     		if(getViewContext().getIdType() != null){
186                 idType = getViewContext().getIdType().toString();
187                 viewContextId = getViewContext().getId();
188                 if(getViewContext().getIdType()==IdType.COPY_OF_OBJECT_ID){
189                 	viewContextId = null;
190                 }
191 
192     		}
193     		
194         	rpcServiceAsync.getMetadata("", null, new KSAsyncCallback<Metadata>(){
195 
196 	        	@Override
197                 public void handleFailure(Throwable caught) {
198 	        		initialized = false;
199                 	onReadyCallback.exec(false);
200                 	KSBlockingProgressIndicator.removeTask(initTask);
201                     throw new RuntimeException("Failed to get model definition.", caught);
202                 }
203 
204                 public void onSuccess(Metadata result) {
205                 	DataModelDefinition def = new DataModelDefinition(result);
206                     cluModel.setDefinition(def);
207                     init(def, onReadyCallback);
208                 }
209 	          });
210             
211         }
212     }
213 
214     private void updateCourseActionItems() {
215     	
216 		for(CourseWorkflowActionList widget: actionDropDownWidgets){
217 			widget.updateCourseActionItems(cluModel);
218 		}
219     }
220 
221     private void init(final DataModelDefinition modelDefinition, final Callback<Boolean> onReadyCallback){
222 
223         CourseRequirementsDataModel.getStatementTypes(new Callback<List<StatementTypeInfo>>() {
224 
225             @Override
226             public void exec(List<StatementTypeInfo> stmtTypes) {
227                 List<StatementTypeInfo> stmtTypesOut = new ArrayList<StatementTypeInfo>();
228                 if (stmtTypes != null) {
229                     for (StatementTypeInfo stmtType : stmtTypes) {
230                         if (stmtType.getId().contains("kuali.statement.type.course.enrollmentEligibility") ||
231                             stmtType.getId().contains("kuali.statement.type.course.creditConstraints")) {
232                             continue;
233                         }
234                         stmtTypesOut.add(stmtType);
235                     }
236                 }
237                 if(!initialized){
238                 	initialized = true;
239 	                cfg.setStatementTypes(stmtTypesOut);
240 	                cfg.setModelDefinition(modelDefinition);
241 	                cfg.generateLayout(ViewCourseController.this);
242                 }
243                 onReadyCallback.exec(true);
244                 KSBlockingProgressIndicator.removeTask(initTask);
245             }
246         });
247     }
248     
249     @SuppressWarnings("unchecked")
250     @Override
251     public void requestModel(Class modelType, final ModelRequestCallback callback) {
252         if(modelType == ReferenceModel.class){
253             if (cluModel != null){
254                 ReferenceModel ref = new ReferenceModel();
255 
256                 if(cluModel.get("course/id") != null){
257                     ref.setReferenceId((String)cluModel.get("course/id"));
258                 } else{
259                     ref.setReferenceId(null);
260                 }
261                 
262                 ref.setReferenceTypeKey(REFERENCE_TYPE);
263                 ref.setReferenceType(cluType);
264                 ref.setReferenceState(CLU_STATE);
265                 
266                 callback.onModelReady(ref);
267             }
268         }else if (modelType == Data.class){
269             requestModel(CourseProposalConfigurer.COURSE_PROPOSAL_MODEL, callback);
270         } else {
271             super.requestModel(modelType, callback);
272         }
273     }
274        
275     @SuppressWarnings("unchecked")    
276     private void getCourseFromCluId(final ModelRequestCallback callback, final Callback<Boolean> workCompleteCallback){
277     	KSBlockingProgressIndicator.addTask(loadDataTask);
278 
279         rpcServiceAsync.getData(courseId, new KSAsyncCallback<Data>(){
280 
281             @Override
282             public void handleFailure(Throwable caught) {
283                 Window.alert("Error loading Course: "+caught.getMessage());
284                 createNewCluModel(callback, workCompleteCallback);
285                 KSBlockingProgressIndicator.removeTask(loadDataTask);
286             }
287 
288             @Override
289             public void onSuccess(Data result) {
290                 cluModel.setRoot(result);
291                 //getContainer().setTitle(getSectionTitle());
292                 setHeaderTitle();
293                 updateCourseActionItems();
294                 callback.onModelReady(cluModel);
295                 workCompleteCallback.exec(true);
296                 reqDataModel.retrieveStatementTypes(cluModel.<String>get("id"), new Callback<Boolean>() {
297                     @Override
298                     public void exec(Boolean result) {
299                         if (result) {
300                             KSBlockingProgressIndicator.removeTask(loadDataTask);
301                         }
302                     }
303                 });
304 
305             }
306 
307         });
308     }
309     
310     @SuppressWarnings("unchecked")
311     private void getCurrentVersion(final ModelRequestCallback callback, final Callback<Boolean> workCompleteCallback) {
312     	rpcServiceAsync.getData(courseId, new KSAsyncCallback<Data>(){
313 
314             @Override
315             public void handleFailure(Throwable caught) {
316                 Window.alert("Error loading Course: "+caught.getMessage());
317                 createNewCluModel(callback, workCompleteCallback);
318                 KSBlockingProgressIndicator.removeTask(loadDataTask);
319             }
320 
321             @Override
322             public void onSuccess(Data result) {
323                 cluModel.setRoot(result);
324                 //getContainer().setTitle(getSectionTitle());
325                 setHeaderTitle();
326                 updateCourseActionItems();
327                 callback.onModelReady(cluModel);
328                 workCompleteCallback.exec(true);
329                 KSBlockingProgressIndicator.removeTask(loadDataTask);
330             }
331 
332         });
333     }
334     
335     @SuppressWarnings("unchecked")
336     private void createNewCluModel(final ModelRequestCallback callback, final Callback<Boolean> workCompleteCallback){
337         cluModel.setRoot(new Data());
338         callback.onModelReady(cluModel);
339         workCompleteCallback.exec(true);
340     }
341 
342     public String getCourseId() {
343         return courseId;
344     }
345     
346     public String getVersionIndId() {
347         return (String)cluModel.get("version/versionIndId");
348     }
349 
350     public void setCourseId(String courseId) {
351         this.courseId = courseId;
352         this.cluModel.setRoot(new Data());        
353     }
354        
355     public void clear(String cluType){
356         super.clear();
357         this.cluType = cluType;
358         if (cluModel != null){
359             this.cluModel.setRoot(new Data());            
360         }
361         this.courseId = null;
362     }    
363     
364     @Override
365     public void showDefaultView(final Callback<Boolean> onReadyCallback) {
366         init(new Callback<Boolean>() {
367 
368             @Override
369             public void exec(Boolean result) {
370                 if (result) {
371                 	ViewCourseController.super.showDefaultView(onReadyCallback);
372                 } else {
373                     onReadyCallback.exec(false);
374                 }
375             }
376         });
377     }
378     
379     @Override
380     public void beforeShow(Callback<Boolean> onReadyCallback) {
381     	showDefaultView(onReadyCallback);
382     }
383     
384     private KSButton getQuitButton(){
385         return new KSButton("Quit", new ClickHandler(){
386                     public void onClick(ClickEvent event) {
387                     	Application.navigate("/HOME/CURRICULUM_HOME");
388                     }
389                 });       
390     }
391     
392     
393     protected void setHeaderTitle() {
394                
395     	String title; 
396     	if (cluModel.get("transcriptTitle") != null){
397     		title = getCourseTitle();
398     	}
399     	else{
400     		title = "Course";
401     	}
402     	
403     	updateStatus();
404     	
405     	this.setContentTitle(title);
406     	this.setName(title);
407     	WindowTitleUtils.setContextTitle(title);
408     }
409     
410     protected void updateStatus() {
411     	if(cluModel.get("stateKey") != null){
412             statusLabel.setText(getMessage("courseStatusLabel") + ": " + cluModel.get("stateKey"));
413     	}
414     }
415     
416     private CloseHandler<KSLightBox> createActionSubmitSuccessHandler() {
417     	CloseHandler<KSLightBox> handler = new CloseHandler<KSLightBox>(){
418 			@Override
419 			public void onClose(CloseEvent<KSLightBox> event) {
420 				//Reload the lum main entrypoint
421 				Window.Location.reload();
422 			}
423     	};
424 		return handler;
425 	}
426 
427     public String getMessage(String courseMessageKey) {
428     	String msg = Application.getApplicationContext().getMessage(MSG_GROUP, courseMessageKey);
429     	if (msg == null) {
430     		msg = courseMessageKey;
431     	}
432     	return msg;
433     }
434     
435 	public Widget getStatusLabel() {
436 		statusLabel.setStyleName("courseStatusLabel");
437 		return statusLabel;
438 	}
439 	
440 	public Widget getVersionHistoryWidget(){
441 		KSButton button = new KSButton("Version History", ButtonStyle.DEFAULT_ANCHOR, new ClickHandler(){
442 
443 			@Override
444 			public void onClick(ClickEvent event) {
445 				parentController.showView(ViewCourseParentController.Views.VERSIONS);
446 			}
447 		});
448 		button.addStyleName("versionHistoryLink");
449 		return button;
450 		
451 	}
452 	
453 	@Override
454 	public void onHistoryEvent(String historyStack) {
455 		super.onHistoryEvent(historyStack);
456 		if (cluModel.get("courseTitle") != null){
457 			RecentlyViewedHelper.addCurrentDocument(getCourseTitle());
458 		}
459 	}
460 	
461 	public String getCourseTitle(){
462 		return cluModel.get("courseTitle");
463 	}
464 
465 	// this is misleading given the current version concept.  This just gets the id of the course
466 	public String getCurrentId() {
467 		return cluModel.get("id");
468 	}
469 
470     @Override
471     public CourseRequirementsDataModel getReqDataModel() {
472         return reqDataModel;
473     }
474     
475     @Override
476     public DataModel getExportDataModel() {
477         return cluModel;
478     }
479     
480     @Override
481     public boolean isExportButtonActive() {
482         return true;
483     }
484     
485     @Override
486     public List<ExportElement> getExportElementsFromView() {
487         List<ExportElement> exportElements = new ArrayList<ExportElement>();
488         ExportElement heading = GWT.create(ExportElement.class);
489         heading.setFieldLabel("");
490         heading.setFieldValue(this.tabPanel.getSelectedTabName());
491         exportElements.add(heading);
492         if (this.getCurrentViewEnum() != null) { 
493             if (this.getCurrentViewEnum().equals(ViewCourseSections.DETAILED)) {
494                 exportElements.addAll(ExportUtils.getDetailsForWidget(this.tabPanel.getSelectedTab(), "", "").get(0).getSubset());
495             } else if (this.getCurrentViewEnum().equals(ViewCourseSections.BRIEF)){
496                 exportElements.addAll(ExportUtils.getDetailsForWidget(this.tabPanel.getSelectedTab(), "", "").get(0).getSubset());
497             } else if (this.getCurrentViewEnum().equals(ViewCourseSections.CATALOG)){
498                 exportElements.addAll(ExportUtils.getDetailsForWidget(this.tabPanel.getSelectedTab(), "", ""));
499             }
500         }
501         return exportElements;
502     }
503     
504     @Override
505     public boolean isAuthorizationRequired() {
506         return true;
507     }
508 
509     @Override
510     public void setAuthorizationRequired(boolean required) {
511         throw new UnsupportedOperationException();
512     }
513     
514     @Override
515     public void checkAuthorization(final AuthorizationCallback authCallback) {
516         Application.getApplicationContext().getSecurityContext().checkScreenPermission(LUUIPermissions.USE_FIND_COURSE_SCREEN, new Callback<Boolean>() {
517             @Override
518             public void exec(Boolean result) {
519 
520                 final boolean isAuthorized = result;
521             
522                 if(isAuthorized){
523                     authCallback.isAuthorized();
524                 }
525                 else
526                     authCallback.isNotAuthorized("User is not authorized: " + LUUIPermissions.USE_FIND_COURSE_SCREEN);
527             }   
528         });
529     }
530 }