Coverage Report - org.kuali.student.lum.lu.ui.course.client.controllers.CourseAdminController
 
Classes in this File Line Coverage Branch Coverage Complexity
CourseAdminController
0%
0/64
0%
0/24
1.875
CourseAdminController$1
0%
0/3
N/A
1.875
CourseAdminController$2
0%
0/3
N/A
1.875
CourseAdminController$3
0%
0/3
N/A
1.875
CourseAdminController$3$1
0%
0/4
0%
0/2
1.875
CourseAdminController$4
0%
0/5
0%
0/2
1.875
CourseAdminController$5
0%
0/4
0%
0/2
1.875
CourseAdminController$5$1
0%
0/8
0%
0/2
1.875
CourseAdminController$6
0%
0/5
0%
0/2
1.875
CourseAdminController$7
0%
0/6
0%
0/2
1.875
CourseAdminController$8
0%
0/3
N/A
1.875
CourseAdminController$9
0%
0/3
N/A
1.875
CourseAdminController$9$1
0%
0/10
0%
0/4
1.875
 
 1  
 package org.kuali.student.lum.lu.ui.course.client.controllers;
 2  
 
 3  
 import java.util.ArrayList;
 4  
 import java.util.List;
 5  
 
 6  
 import org.kuali.student.common.assembly.data.QueryPath;
 7  
 import org.kuali.student.common.dto.DtoConstants;
 8  
 import org.kuali.student.common.ui.client.application.Application;
 9  
 import org.kuali.student.common.ui.client.application.ViewContext;
 10  
 import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
 11  
 import org.kuali.student.common.ui.client.configurable.mvc.sections.Section;
 12  
 import org.kuali.student.common.ui.client.configurable.mvc.views.VerticalSectionView;
 13  
 import org.kuali.student.common.ui.client.event.ActionEvent;
 14  
 import org.kuali.student.common.ui.client.event.SaveActionEvent;
 15  
 import org.kuali.student.common.ui.client.mvc.ActionCompleteCallback;
 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.ModelRequestCallback;
 20  
 import org.kuali.student.common.ui.client.mvc.View;
 21  
 import org.kuali.student.common.ui.client.util.WindowTitleUtils;
 22  
 import org.kuali.student.common.ui.client.widgets.KSButton;
 23  
 import org.kuali.student.common.ui.client.widgets.menus.KSMenuItemData;
 24  
 import org.kuali.student.common.ui.client.widgets.notification.KSNotification;
 25  
 import org.kuali.student.common.ui.client.widgets.notification.KSNotifier;
 26  
 import org.kuali.student.common.ui.shared.IdAttributes.IdType;
 27  
 import org.kuali.student.common.validation.dto.ValidationResultInfo;
 28  
 import org.kuali.student.core.workflow.ui.client.widgets.WorkflowUtilities;
 29  
 import org.kuali.student.lum.common.client.lu.LUUIConstants;
 30  
 import org.kuali.student.lum.common.client.widgets.AppLocations;
 31  
 import org.kuali.student.lum.lu.LUConstants;
 32  
 import org.kuali.student.lum.lu.assembly.data.client.constants.orch.CreditCourseConstants;
 33  
 import org.kuali.student.lum.lu.ui.course.client.configuration.CourseAdminConfigurer;
 34  
 import org.kuali.student.lum.lu.ui.course.client.configuration.CourseProposalConfigurer;
 35  
 import org.kuali.student.lum.lu.ui.course.client.configuration.CourseProposalConfigurer.CourseSections;
 36  
 
 37  
 import com.google.gwt.core.client.GWT;
 38  
 import com.google.gwt.event.dom.client.ClickEvent;
 39  
 import com.google.gwt.event.dom.client.ClickHandler;
 40  
 import com.google.gwt.user.client.DOM;
 41  
 import com.google.gwt.user.client.ui.Widget;
 42  
 
 43  
 /**
 44  
  * Controller for create/modify course with proposal wrapper admin screens. This controller uses a different
 45  
  * configurer for admin screens and attempts to reuse as much of the validation, save & retreive logic coded
 46  
  * in the CourseProposalController.  Also it reuses the menu from CourseProposalController and adds click
 47  
  * handlers to button menu to navigate user b/w sections of the same view.
 48  
  * 
 49  
  * @author Will
 50  
  *
 51  
  */
 52  0
 public class CourseAdminController extends CourseProposalController{
 53  
         
 54  
         //Need to keep track of cancel buttons, so they can be enabled when course has been saved. 
 55  0
         List<KSButton> cancelButtons = new ArrayList<KSButton>();
 56  
         
 57  
         /**
 58  
          * Override the intitailzeController method to use CourseAdminConfigurer 
 59  
          */
 60  
         @Override
 61  
         protected void initializeController() {
 62  
                    
 63  0
                 super.cfg = GWT.create(CourseAdminConfigurer.class);
 64  0
                 super.proposalPath = cfg.getProposalPath();
 65  0
                    super.workflowUtil = new WorkflowUtilities(CourseAdminController.this ,proposalPath);
 66  
                 
 67  0
                    cfg.setState(DtoConstants.STATE_DRAFT.toUpperCase());
 68  0
                    cfg.setNextState(DtoConstants.STATE_APPROVED.toUpperCase());
 69  0
                    super.setDefaultModelId(cfg.getModelId());
 70  0
                    super.registerModelsAndHandlers();
 71  0
                    super.addStyleName("ks-course-admin");
 72  0
                    currentDocType = LUConstants.PROPOSAL_TYPE_COURSE_CREATE_ADMIN;                           
 73  0
     }
 74  
 
 75  
         /**
 76  
          * Override the getSaveButton to provide a new set of buttons for the admin screens
 77  
          */
 78  
         @Override
 79  
         public KSButton getSaveButton(){
 80  0
                 KSButton saveButton = new KSButton("Save", new ClickHandler(){
 81  
             public void onClick(ClickEvent event) {
 82  0
                     handleButtonClick(DtoConstants.STATE_DRAFT);                    
 83  0
             }
 84  
         });                
 85  
                 
 86  0
                 saveButton.addStyleName("ks-button-spacing");
 87  0
                 return saveButton;
 88  
         }
 89  
                 
 90  
         public KSButton getApproveAndActivateButton(){
 91  0
                 return new KSButton("Approve and Activate", new ClickHandler(){
 92  
             public void onClick(ClickEvent event) {
 93  0
                     handleButtonClick(DtoConstants.STATE_ACTIVE);
 94  0
             }
 95  
         });                
 96  
         }
 97  
 
 98  
         public KSButton getCancelButton(){
 99  0
                 KSButton cancelButton = new KSButton("Cancel Proposal", new ClickHandler(){
 100  
             public void onClick(ClickEvent event) {
 101  
                     //Cancel the proposal and navigate the user back to curriculum home if cancel was successful.
 102  0
                     workflowUtil.cancel(new Callback<Boolean>(){
 103  
                                         @Override
 104  
                                         public void exec(Boolean result) {
 105  0
                                                 if (result){
 106  0
                                                         Application.navigate(AppLocations.Locations.CURRICULUM_MANAGEMENT.getLocation());                                                        
 107  
                                                 }                                                
 108  0
                                         }
 109  
                             
 110  
                     });
 111  0
             }
 112  
         });
 113  
         
 114  0
                 if (LUConstants.PROPOSAL_TYPE_COURSE_CREATE_ADMIN.equals(currentDocType) || 
 115  
                                 LUConstants.PROPOSAL_TYPE_COURSE_MODIFY_ADMIN.equals(currentDocType)){
 116  
                         //For new admin proposal, disable the cancel button intially since proposal doesn't exist
 117  
                         //until they click save.
 118  0
                         cancelButton.setEnabled(false);
 119  
                 }
 120  
                 
 121  0
                 cancelButton.addStyleName("ks-button-spacing");
 122  0
                 cancelButtons.add(cancelButton);
 123  0
                 return cancelButton;
 124  
     }
 125  
         
 126  
         /**
 127  
          * Processes the save, approve, or approve and activate button clicks. The action is determined
 128  
          * by the value of the state parameter.
 129  
          * 
 130  
          * @param state The state to set on the course when saving course data. DRAFT=Save, APPROVED=Approve, and
 131  
          * ACTIVE=Approve & Activate
 132  
          */
 133  
         protected void handleButtonClick(final String state){
 134  
                 
 135  
             //Set state on course before performing save action
 136  0
                 cluProposalModel.set(QueryPath.parse(CreditCourseConstants.STATE), state);
 137  
             
 138  0
             final SaveActionEvent saveActionEvent = getSaveActionEvent(state);
 139  
             
 140  
             //Store the rules if save was called
 141  0
             if((String)cluProposalModel.get(CreditCourseConstants.ID)!=null && cfg instanceof CourseAdminConfigurer){
 142  0
                     ((CourseAdminConfigurer )cfg).getRequisitesSection().storeRules(new Callback<Boolean>(){
 143  
                             public void exec(Boolean result) {
 144  0
                                         if(result){
 145  0
                                                 doAdminSaveAction(saveActionEvent, state);
 146  
                                         }else{
 147  0
                                                 KSNotifier.show("Error saving rules.");
 148  
                                         }
 149  0
                                 }
 150  
                     });
 151  
             }else{
 152  0
             doAdminSaveAction(saveActionEvent, state);                    
 153  
             }
 154  0
         }
 155  
                 
 156  
         /**
 157  
          * Returns a SaveActionEvent with the appropriate ActionCompleteCallback, which will take additional admin actions once
 158  
          * save is complete. The action (i.e. button clicked) is determined by the value of the state parameter 
 159  
          * 
 160  
          * @param state The state to set on the course when saving course data. DRAFT=Save, APPROVED=Approve, and
 161  
          * ACTIVE=Approve & Activate
 162  
          * @return the save event that will be fired based on the button click
 163  
          */
 164  
         private SaveActionEvent getSaveActionEvent(final String state){
 165  0
             final SaveActionEvent saveActionEvent = new SaveActionEvent(false);
 166  0
                 if (DtoConstants.STATE_APPROVED.equalsIgnoreCase(state) || DtoConstants.STATE_ACTIVE.equalsIgnoreCase(state)){
 167  
                 // For "Approve" and "Approve & Activate" actions, automatically blanket approve the admin proposal so it 
 168  
                 // enters final state. This is accomplished by first saving the course (via saveActionEvent) and then by 
 169  
                 // executing the the blanket approve call upon a successful save. When user clicks either of these buttons 
 170  
                 // and blanket approve is successful, they are navigated to the view course screen.
 171  
 
 172  0
                     saveActionEvent.setActionCompleteCallback(new ActionCompleteCallback(){
 173  
                                 @Override
 174  
                                 public void onActionComplete(ActionEvent actionEvent) {
 175  0
                                         if (saveActionEvent.isSaveSuccessful()){
 176  0
                                                 workflowUtil.blanketApprove(new Callback<Boolean>(){
 177  
                                                         @Override
 178  
                                                         public void exec(Boolean result) {
 179  
                                                             // FIXME:  Even though workflow rpc call to blanket approve is successful, the
 180  
                                                                 // asynchronous nature of workflow is causing timing issues here. Need to investigate
 181  
                                                             // making blanket approve workflow more synchronous to avoid the timing issues.
 182  
                                                                 // NOTE: One solution is to not allow an activate here, and force user to activate from view
 183  
                                                                 // screen, the down side being user now requires two clicks.
 184  
                                                                 
 185  0
                                                                 final ViewContext viewContext = new ViewContext();
 186  0
                                                 viewContext.setId((String)cluProposalModel.get(CreditCourseConstants.ID));
 187  0
                                                 viewContext.setIdType(IdType.OBJECT_ID);                                                                                                                        
 188  0
                                                                 if (DtoConstants.STATE_ACTIVE.equalsIgnoreCase(state)){
 189  0
                                                                         KSNotifier.show("Course approved and activated. It may take a minute or two for course status to be updated. Refresh to see latest status.");
 190  0
                                                                         Application.navigate(AppLocations.Locations.VIEW_COURSE.getLocation(), viewContext);
 191  
                                                                 }
 192  
                                                                 
 193  0
                                                         }
 194  
                                                 });
 195  
                                         }      
 196  0
                                 }
 197  
                     });
 198  
             } else {
 199  
                     //User clicked Save button. When user clicks save, both document upload and cancel should be enabled
 200  0
                     saveActionEvent.setActionCompleteCallback(new ActionCompleteCallback(){
 201  
                                 @Override
 202  
                                 public void onActionComplete(ActionEvent action) {
 203  0
                                         for (KSButton cancelButton:cancelButtons){
 204  0
                                                 cancelButton.setEnabled(true);
 205  0
                                                 ((CourseAdminConfigurer )cfg).getDocumentTool().beforeShow(NO_OP_CALLBACK);
 206  
                                         }                                        
 207  0
                                 }                            
 208  
                     });
 209  
             }
 210  
                 
 211  0
                 return saveActionEvent;
 212  
         }
 213  
         
 214  
         /**
 215  
          * Fires the SaveActionEvent to be handled by the {@link CourseProposalController}. 
 216  
          * 
 217  
          *  @see CourseProposalController#registerModelsAndHandlers()
 218  
          *  @param saveActionEvent SaveActionEvent to fire
 219  
          *  @param state The state to set on the course when saving course data. DRAFT=Save, APPROVED=Approve, and
 220  
          *  ACTIVE=Approve & Activate
 221  
          */
 222  
         private void doAdminSaveAction(final SaveActionEvent saveActionEvent, String state){
 223  0
                 if (DtoConstants.STATE_APPROVED.equalsIgnoreCase(state) || DtoConstants.STATE_ACTIVE.equalsIgnoreCase(state)){
 224  
                         //For Approved action, validate required fields for next (i.e.Approved) state before firing the save action
 225  0
                         cluProposalModel.validateNextState((new Callback<List<ValidationResultInfo>>() {
 226  
                     @Override
 227  
                     public void exec(List<ValidationResultInfo> result) {
 228  
         
 229  0
                             boolean isSectionValid = isValid(result, true);
 230  
         
 231  0
                             if(isSectionValid){
 232  0
                                     CourseAdminController.this.fireApplicationEvent(saveActionEvent);                    }
 233  
                             else{
 234  0
                                     KSNotifier.add(new KSNotification("Unable to save, please check fields for errors.", false, true, 5000));
 235  
                             }
 236  
         
 237  0
                     }
 238  
                 }));
 239  
                 } else {
 240  0
                     CourseAdminController.this.fireApplicationEvent(saveActionEvent);                        
 241  
                 }                 
 242  0
         }
 243  
         
 244  
         /**
 245  
      * Override the setHeaderTitle to display proper header title for admin screens
 246  
      */
 247  
         @Override
 248  
         protected void setHeaderTitle(){
 249  
             String title;
 250  0
             if (cluProposalModel.get(cfg.getProposalTitlePath()) != null){
 251  0
                     title = getProposalTitle();
 252  
             }
 253  
             else{
 254  0
                     title = "New Course (Admin Proposal)";
 255  
             }
 256  0
             this.setContentTitle(title);
 257  0
             this.setName(title);
 258  0
             WindowTitleUtils.setContextTitle(title);
 259  0
                 currentTitle = title;
 260  0
     }
 261  
     
 262  
         private String getProposalTitle(){
 263  0
                 StringBuffer sb = new StringBuffer();
 264  0
                 sb.append(cluProposalModel.get(cfg.getProposalTitlePath()));
 265  0
                 sb.append(" (Admin Proposal)");
 266  0
                 return sb.toString();
 267  
         }
 268  
         
 269  
         /**
 270  
          * This is a special method for CourseAdminController, which adds a menu item to the navigation menu and navigates
 271  
          * a user to a section within the view rather than a different view.
 272  
          * 
 273  
          * @param parentMenu
 274  
          * @param sectionName
 275  
          * @param sectionId
 276  
          * @param section
 277  
          */
 278  
     public void addMenuItemSection(String parentMenu, final String sectionName, final String widgetId, final Widget widget) {            
 279  0
         KSMenuItemData parentItem = null;
 280  0
         for (int i = 0; i < topLevelMenuItems.size(); i++) {
 281  0
             if (topLevelMenuItems.get(i).getLabel().equals(parentMenu)) {
 282  0
                 parentItem = topLevelMenuItems.get(i);
 283  0
                 break;
 284  
             }
 285  
         }
 286  
 
 287  0
         KSMenuItemData item = new KSMenuItemData(sectionName);
 288  0
             widget.getElement().setId(widgetId);
 289  0
             item.setClickHandler(new ClickHandler(){
 290  
                         public void onClick(ClickEvent event) {                
 291  
                                 //FIXME: This doesn't scroll to exactly the position stuff
 292  0
                                 DOM.getElementById(widgetId).scrollIntoView();
 293  0
                         }                    
 294  
             });
 295  
 
 296  0
         if (parentItem != null) {
 297  0
             parentItem.addSubItem(item);
 298  
         } else {
 299  0
             topLevelMenuItems.add(item);
 300  
         }
 301  
 
 302  0
         menu.refresh();
 303  0
     }
 304  
 
 305  
         @Override
 306  
         protected void configureScreens(DataModelDefinition modelDefinition,
 307  
                         final Callback<Boolean> onReadyCallback) {
 308  0
                 super.configureScreens(modelDefinition, new Callback<Boolean>(){
 309  
 
 310  
                         @Override
 311  
                         public void exec(final Boolean result) {
 312  0
                                 requestModel(new ModelRequestCallback<DataModel>(){
 313  
                                         @Override
 314  
                                         public void onModelReady(DataModel model) {
 315  0
                                                 String versionedFromId = model.get("versionInfo/versionedFromId");
 316  0
                                                 if(versionedFromId!=null && !versionedFromId.isEmpty()){
 317  0
                                                         VerticalSectionView view = (VerticalSectionView) viewMap.get(CourseSections.COURSE_INFO);
 318  0
                                                         Section activeDatesSection = view.getSection(LUUIConstants.ACTIVE_DATES_LABEL_KEY);
 319  0
                                                         FieldDescriptor fd = cfg.addField(activeDatesSection, CourseProposalConfigurer.PROPOSAL_PATH + "/" + CreditCourseConstants.PREV_END_TERM, cfg.generateMessageInfo(LUUIConstants.PROPOSAL_PREV_END_TERM));
 320  0
                                                         WorkflowUtilities.updateCrossField(fd, model);
 321  
                                                 }
 322  0
                                                 onReadyCallback.exec(result);
 323  0
                                         }
 324  
                                         @Override
 325  
                                         public void onRequestFail(Throwable cause) {
 326  0
                                                 throw new RuntimeException("Error getting model",cause);
 327  
                                         }
 328  
                                 });
 329  0
                         }
 330  
                 });
 331  0
         }
 332  
 
 333  
 }