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.PermissionType;
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.setPermissionType(PermissionType.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 = new CourseWorkflowActionList(this.getMessage("cluActionsLabel"), getViewContext(), "/HOME/CURRICULUM_HOME/COURSE_PROPOSAL", cluModel, new Callback<String>() {
155     		@Override
156     		public void exec(String newState) {
157     			if (newState != null) {
158                     KSNotifier.add(new KSNotification(getMessage("cluStateChangeNotification" + newState), false, 5000));
159                     // FIXME: this is not updating the cluModel so state will not be updated in the model.  May not be a problem.
160                             statusLabel.setText("Course Status: " + newState);
161     			} else {
162                     KSNotifier.add(new KSNotification(getMessage("cluStateChangeFailedNotification"), false, 5000));
163     			}
164     		}
165     	});
166         actionDropDownWidgets.add(actionList);
167         
168     	return actionList;
169     }
170    
171     private void init(final Callback<Boolean> onReadyCallback) {
172 
173         if (initialized) {
174             onReadyCallback.exec(true);
175         } else {
176         	KSBlockingProgressIndicator.addTask(initTask);
177         	this.setContentTitle("View Course");
178     		this.setName("Course");
179     		String idType = null;
180     		String viewContextId = null;
181     		// The switch was added due to the way permissions currently work.
182     		// For a new Create Course Proposal or Modify Course we send nulls so that permissions are not checked.
183     		if(getViewContext().getIdType() != null){
184                 idType = getViewContext().getIdType().toString();
185                 viewContextId = getViewContext().getId();
186                 if(getViewContext().getIdType()==IdType.COPY_OF_OBJECT_ID){
187                 	viewContextId = null;
188                 }
189 
190     		}
191     		
192         	rpcServiceAsync.getMetadata("", null, new KSAsyncCallback<Metadata>(){
193 
194 	        	@Override
195                 public void handleFailure(Throwable caught) {
196 	        		initialized = false;
197                 	onReadyCallback.exec(false);
198                 	KSBlockingProgressIndicator.removeTask(initTask);
199                     throw new RuntimeException("Failed to get model definition.", caught);
200                 }
201 
202                 public void onSuccess(Metadata result) {
203                 	DataModelDefinition def = new DataModelDefinition(result);
204                     cluModel.setDefinition(def);
205                     init(def, onReadyCallback);
206                 }
207 	          });
208             
209         }
210     }
211 
212     private void updateCourseActionItems() {
213     	
214 		for(CourseWorkflowActionList widget: actionDropDownWidgets){
215 			widget.updateCourseActionItems(cluModel);
216 		}
217     }
218 
219     private void init(final DataModelDefinition modelDefinition, final Callback<Boolean> onReadyCallback){
220 
221         CourseRequirementsDataModel.getStatementTypes(new Callback<List<StatementTypeInfo>>() {
222 
223             @Override
224             public void exec(List<StatementTypeInfo> stmtTypes) {
225                 List<StatementTypeInfo> stmtTypesOut = new ArrayList<StatementTypeInfo>();
226                 if (stmtTypes != null) {
227                     for (StatementTypeInfo stmtType : stmtTypes) {
228                         if (stmtType.getId().contains("kuali.statement.type.course.enrollmentEligibility") ||
229                             stmtType.getId().contains("kuali.statement.type.course.creditConstraints")) {
230                             continue;
231                         }
232                         stmtTypesOut.add(stmtType);
233                     }
234                 }
235                 if(!initialized){
236                 	initialized = true;
237 	                cfg.setStatementTypes(stmtTypesOut);
238 	                cfg.setModelDefinition(modelDefinition);
239 	                cfg.generateLayout(ViewCourseController.this);
240                 }
241                 onReadyCallback.exec(true);
242                 KSBlockingProgressIndicator.removeTask(initTask);
243             }
244         });
245     }
246     
247     @SuppressWarnings("unchecked")
248     @Override
249     public void requestModel(Class modelType, final ModelRequestCallback callback) {
250         if(modelType == ReferenceModel.class){
251             if (cluModel != null){
252                 ReferenceModel ref = new ReferenceModel();
253 
254                 if(cluModel.get("course/id") != null){
255                     ref.setReferenceId((String)cluModel.get("course/id"));
256                 } else{
257                     ref.setReferenceId(null);
258                 }
259                 
260                 ref.setReferenceTypeKey(REFERENCE_TYPE);
261                 ref.setReferenceType(cluType);
262                 ref.setReferenceState(CLU_STATE);
263                 
264                 callback.onModelReady(ref);
265             }
266         }else if (modelType == Data.class){
267             requestModel(CourseProposalConfigurer.COURSE_PROPOSAL_MODEL, callback);
268         } else {
269             super.requestModel(modelType, callback);
270         }
271     }
272        
273     @SuppressWarnings("unchecked")    
274     private void getCourseFromCluId(final ModelRequestCallback callback, final Callback<Boolean> workCompleteCallback){
275     	KSBlockingProgressIndicator.addTask(loadDataTask);
276 
277         rpcServiceAsync.getData(courseId, new KSAsyncCallback<Data>(){
278 
279             @Override
280             public void handleFailure(Throwable caught) {
281                 Window.alert("Error loading Course: "+caught.getMessage());
282                 createNewCluModel(callback, workCompleteCallback);
283                 KSBlockingProgressIndicator.removeTask(loadDataTask);
284             }
285 
286             @Override
287             public void onSuccess(Data result) {
288                 cluModel.setRoot(result);
289                 //getContainer().setTitle(getSectionTitle());
290                 setHeaderTitle();
291                 updateCourseActionItems();
292                 callback.onModelReady(cluModel);
293                 workCompleteCallback.exec(true);
294                 reqDataModel.retrieveStatementTypes(cluModel.<String>get("id"), new Callback<Boolean>() {
295                     @Override
296                     public void exec(Boolean result) {
297                         if (result) {
298                             KSBlockingProgressIndicator.removeTask(loadDataTask);
299                         }
300                     }
301                 });
302 
303             }
304 
305         });
306     }
307     
308     @SuppressWarnings("unchecked")
309     private void getCurrentVersion(final ModelRequestCallback callback, final Callback<Boolean> workCompleteCallback) {
310     	rpcServiceAsync.getData(courseId, new KSAsyncCallback<Data>(){
311 
312             @Override
313             public void handleFailure(Throwable caught) {
314                 Window.alert("Error loading Course: "+caught.getMessage());
315                 createNewCluModel(callback, workCompleteCallback);
316                 KSBlockingProgressIndicator.removeTask(loadDataTask);
317             }
318 
319             @Override
320             public void onSuccess(Data result) {
321                 cluModel.setRoot(result);
322                 //getContainer().setTitle(getSectionTitle());
323                 setHeaderTitle();
324                 updateCourseActionItems();
325                 callback.onModelReady(cluModel);
326                 workCompleteCallback.exec(true);
327                 KSBlockingProgressIndicator.removeTask(loadDataTask);
328             }
329 
330         });
331     }
332     
333     @SuppressWarnings("unchecked")
334     private void createNewCluModel(final ModelRequestCallback callback, final Callback<Boolean> workCompleteCallback){
335         cluModel.setRoot(new Data());
336         callback.onModelReady(cluModel);
337         workCompleteCallback.exec(true);
338     }
339 
340     public String getCourseId() {
341         return courseId;
342     }
343     
344     public String getVersionIndId() {
345         return (String)cluModel.get("version/versionIndId");
346     }
347 
348     public void setCourseId(String courseId) {
349         this.courseId = courseId;
350         this.cluModel.setRoot(new Data());        
351     }
352        
353     public void clear(String cluType){
354         super.clear();
355         this.cluType = cluType;
356         if (cluModel != null){
357             this.cluModel.setRoot(new Data());            
358         }
359         this.courseId = null;
360     }    
361     
362     @Override
363     public void showDefaultView(final Callback<Boolean> onReadyCallback) {
364         init(new Callback<Boolean>() {
365 
366             @Override
367             public void exec(Boolean result) {
368                 if (result) {
369                 	ViewCourseController.super.showDefaultView(onReadyCallback);
370                 } else {
371                     onReadyCallback.exec(false);
372                 }
373             }
374         });
375     }
376     
377     @Override
378     public void beforeShow(Callback<Boolean> onReadyCallback) {
379     	showDefaultView(onReadyCallback);
380     }
381     
382     private KSButton getQuitButton(){
383         return new KSButton("Quit", new ClickHandler(){
384                     public void onClick(ClickEvent event) {
385                     	Application.navigate("/HOME/CURRICULUM_HOME");
386                     }
387                 });       
388     }
389     
390     
391     protected void setHeaderTitle() {
392                
393     	String title; 
394     	if (cluModel.get("transcriptTitle") != null){
395     		title = getCourseTitle();
396     	}
397     	else{
398     		title = "Course";
399     	}
400     	
401     	updateStatus();
402     	
403     	this.setContentTitle(title);
404     	this.setName(title);
405     	WindowTitleUtils.setContextTitle(title);
406     }
407     
408     protected void updateStatus() {
409     	if(cluModel.get("stateKey") != null){
410             statusLabel.setText(getMessage("courseStatusLabel") + ": " + cluModel.get("stateKey"));
411     	}
412     }
413     
414     private CloseHandler<KSLightBox> createActionSubmitSuccessHandler() {
415     	CloseHandler<KSLightBox> handler = new CloseHandler<KSLightBox>(){
416 			@Override
417 			public void onClose(CloseEvent<KSLightBox> event) {
418 				//Reload the lum main entrypoint
419 				Window.Location.reload();
420 			}
421     	};
422 		return handler;
423 	}
424 
425     public String getMessage(String courseMessageKey) {
426     	String msg = Application.getApplicationContext().getMessage(MSG_GROUP, courseMessageKey);
427     	if (msg == null) {
428     		msg = courseMessageKey;
429     	}
430     	return msg;
431     }
432     
433 	public Widget getStatusLabel() {
434 		statusLabel.setStyleName("courseStatusLabel");
435 		return statusLabel;
436 	}
437 	
438 	public Widget getVersionHistoryWidget(){
439 		KSButton button = new KSButton("Version History", ButtonStyle.DEFAULT_ANCHOR, new ClickHandler(){
440 
441 			@Override
442 			public void onClick(ClickEvent event) {
443 				parentController.showView(ViewCourseParentController.Views.VERSIONS);
444 			}
445 		});
446 		button.addStyleName("versionHistoryLink");
447 		return button;
448 		
449 	}
450 	
451 	@Override
452 	public void onHistoryEvent(String historyStack) {
453 		super.onHistoryEvent(historyStack);
454 		if (cluModel.get("courseTitle") != null){
455 			RecentlyViewedHelper.addCurrentDocument(getCourseTitle());
456 		}
457 	}
458 	
459 	public String getCourseTitle(){
460 		return cluModel.get("courseTitle");
461 	}
462 
463 	// this is misleading given the current version concept.  This just gets the id of the course
464 	public String getCurrentId() {
465 		return cluModel.get("id");
466 	}
467 
468     @Override
469     public CourseRequirementsDataModel getReqDataModel() {
470         return reqDataModel;
471     }
472     
473     @Override
474     public DataModel getExportDataModel() {
475         return cluModel;
476     }
477     
478     @Override
479     public boolean isExportButtonActive() {
480         return true;
481     }
482     
483     @Override
484     public List<ExportElement> getExportElementsFromView() {
485         List<ExportElement> exportElements = new ArrayList<ExportElement>();
486         ExportElement heading = GWT.create(ExportElement.class);
487         heading.setFieldLabel("");
488         heading.setFieldValue(this.tabPanel.getSelectedTabName());
489         exportElements.add(heading);
490         if (this.getCurrentViewEnum() != null) { 
491             if (this.getCurrentViewEnum().equals(ViewCourseSections.DETAILED)) {
492                 exportElements.addAll(ExportUtils.getDetailsForWidget(this.tabPanel.getSelectedTab(), "", "").get(0).getSubset());
493             } else if (this.getCurrentViewEnum().equals(ViewCourseSections.BRIEF)){
494                 exportElements.addAll(ExportUtils.getDetailsForWidget(this.tabPanel.getSelectedTab(), "", "").get(0).getSubset());
495             } else if (this.getCurrentViewEnum().equals(ViewCourseSections.CATALOG)){
496                 exportElements.addAll(ExportUtils.getDetailsForWidget(this.tabPanel.getSelectedTab(), "", ""));
497             }
498         }
499         return exportElements;
500     }
501     
502     @Override
503     public boolean isAuthorizationRequired() {
504         return true;
505     }
506 
507     @Override
508     public void setAuthorizationRequired(boolean required) {
509         throw new UnsupportedOperationException();
510     }
511     
512     @Override
513     public void checkAuthorization(final AuthorizationCallback authCallback) {
514         Application.getApplicationContext().getSecurityContext().checkScreenPermission(LUUIPermissions.USE_FIND_COURSE_SCREEN, new Callback<Boolean>() {
515             @Override
516             public void exec(Boolean result) {
517 
518                 final boolean isAuthorized = result;
519             
520                 if(isAuthorized){
521                     authCallback.isAuthorized();
522                 }
523                 else
524                     authCallback.isNotAuthorized("User is not authorized: " + LUUIPermissions.USE_FIND_COURSE_SCREEN);
525             }   
526         });
527     }
528 }