Coverage Report - org.kuali.student.lum.lu.ui.course.client.controllers.CourseAdminController
 
Classes in this File Line Coverage Branch Coverage Complexity
CourseAdminController
0%
0/81
0%
0/34
2.034
CourseAdminController$1
0%
0/3
N/A
2.034
CourseAdminController$2
0%
0/3
N/A
2.034
CourseAdminController$3
0%
0/5
0%
0/2
2.034
CourseAdminController$3$1
0%
0/4
0%
0/2
2.034
CourseAdminController$4
0%
0/5
0%
0/2
2.034
CourseAdminController$5
0%
0/4
0%
0/2
2.034
CourseAdminController$5$1
0%
0/8
0%
0/2
2.034
CourseAdminController$6
0%
0/5
0%
0/2
2.034
CourseAdminController$7
0%
0/6
0%
0/2
2.034
CourseAdminController$8
0%
0/4
N/A
2.034
CourseAdminController$9
0%
0/3
N/A
2.034
CourseAdminController$9$1
0%
0/15
0%
0/10
2.034
 
 1  
 package org.kuali.student.lum.lu.ui.course.client.controllers;
 2  
 
 3  
 import java.util.ArrayList;
 4  
 import java.util.List;
 5  
 import java.util.Map;
 6  
 
 7  
 import org.kuali.student.common.assembly.data.Metadata;
 8  
 import org.kuali.student.common.assembly.data.QueryPath;
 9  
 import org.kuali.student.common.dto.DtoConstants;
 10  
 import org.kuali.student.common.rice.StudentIdentityConstants;
 11  
 import org.kuali.student.common.rice.authorization.PermissionType;
 12  
 import org.kuali.student.common.ui.client.application.Application;
 13  
 import org.kuali.student.common.ui.client.application.ViewContext;
 14  
 import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
 15  
 import org.kuali.student.common.ui.client.configurable.mvc.sections.Section;
 16  
 import org.kuali.student.common.ui.client.configurable.mvc.views.VerticalSectionView;
 17  
 import org.kuali.student.common.ui.client.event.ActionEvent;
 18  
 import org.kuali.student.common.ui.client.event.SaveActionEvent;
 19  
 import org.kuali.student.common.ui.client.mvc.ActionCompleteCallback;
 20  
 import org.kuali.student.common.ui.client.mvc.Callback;
 21  
 import org.kuali.student.common.ui.client.mvc.DataModel;
 22  
 import org.kuali.student.common.ui.client.mvc.DataModelDefinition;
 23  
 import org.kuali.student.common.ui.client.mvc.ModelRequestCallback;
 24  
 import org.kuali.student.common.ui.client.util.WindowTitleUtils;
 25  
 import org.kuali.student.common.ui.client.widgets.KSButton;
 26  
 import org.kuali.student.common.ui.client.widgets.menus.KSMenuItemData;
 27  
 import org.kuali.student.common.ui.client.widgets.notification.KSNotification;
 28  
 import org.kuali.student.common.ui.client.widgets.notification.KSNotifier;
 29  
 import org.kuali.student.common.ui.shared.IdAttributes.IdType;
 30  
 import org.kuali.student.common.validation.dto.ValidationResultInfo;
 31  
 import org.kuali.student.core.workflow.ui.client.widgets.WorkflowUtilities;
 32  
 import org.kuali.student.lum.common.client.lu.LUUIConstants;
 33  
 import org.kuali.student.lum.common.client.widgets.AppLocations;
 34  
 import org.kuali.student.lum.lu.LUConstants;
 35  
 import org.kuali.student.lum.lu.assembly.data.client.constants.orch.CreditCourseConstants;
 36  
 import org.kuali.student.lum.lu.ui.course.client.configuration.CourseAdminConfigurer;
 37  
 import org.kuali.student.lum.lu.ui.course.client.configuration.CourseProposalConfigurer;
 38  
 import org.kuali.student.lum.lu.ui.course.client.configuration.CourseProposalConfigurer.CourseSections;
 39  
 
 40  
 import com.google.gwt.core.client.GWT;
 41  
 import com.google.gwt.event.dom.client.ClickEvent;
 42  
 import com.google.gwt.event.dom.client.ClickHandler;
 43  
 import com.google.gwt.user.client.DOM;
 44  
 import com.google.gwt.user.client.Element;
 45  
 import com.google.gwt.user.client.ui.Widget;
 46  
 
 47  
 /**
 48  
  * Controller for create/modify course with proposal wrapper admin screens. This controller uses a different
 49  
  * configurer for admin screens and attempts to reuse as much of the validation, save & retreive logic coded
 50  
  * in the CourseProposalController.  Also it reuses the menu from CourseProposalController and adds click
 51  
  * handlers to button menu to navigate user b/w sections of the same view.
 52  
  * 
 53  
  * @author Will
 54  
  *
 55  
  */
 56  0
 public class CourseAdminController extends CourseProposalController{
 57  
         
 58  
         //Need to keep track of cancel buttons, so they can be enabled when course has been saved. 
 59  0
         List<KSButton> cancelButtons = new ArrayList<KSButton>();
 60  
         
 61  
         /**
 62  
          * Override the intitailzeController method to use CourseAdminConfigurer 
 63  
          */
 64  
         @Override
 65  
         protected void initializeController() {
 66  
                    
 67  0
                 super.cfg = GWT.create(CourseAdminConfigurer.class);
 68  0
                 super.proposalPath = cfg.getProposalPath();
 69  0
                    super.workflowUtil = new WorkflowUtilities(CourseAdminController.this ,proposalPath);
 70  
                 
 71  0
                    cfg.setState(DtoConstants.STATE_DRAFT.toUpperCase());
 72  0
                    cfg.setNextState(DtoConstants.STATE_APPROVED.toUpperCase());
 73  0
                    super.setDefaultModelId(cfg.getModelId());
 74  0
                    super.registerModelsAndHandlers();
 75  0
                    super.addStyleName("ks-course-admin");
 76  0
                    currentDocType = LUConstants.PROPOSAL_TYPE_COURSE_CREATE_ADMIN;         
 77  
                    
 78  0
         setViewContext(getViewContext());
 79  0
     }
 80  
 
 81  
         /**
 82  
          * Override the getSaveButton to provide a new set of buttons for the admin screens
 83  
          */
 84  
         @Override
 85  
         public KSButton getSaveButton(){
 86  0
                 KSButton saveButton = new KSButton("Save", new ClickHandler(){
 87  
             public void onClick(ClickEvent event) {
 88  0
                     handleButtonClick(DtoConstants.STATE_DRAFT);                    
 89  0
             }
 90  
         });                
 91  
                 
 92  0
                 saveButton.addStyleName("ks-button-spacing");
 93  0
                 return saveButton;
 94  
         }
 95  
                 
 96  
         public KSButton getApproveAndActivateButton(){
 97  0
                 return new KSButton("Approve and Activate", new ClickHandler(){
 98  
             public void onClick(ClickEvent event) {
 99  0
                     handleButtonClick(DtoConstants.STATE_ACTIVE);
 100  0
             }
 101  
         });                
 102  
         }
 103  
 
 104  
         public KSButton getCancelButton(){
 105  0
                 KSButton cancelButton = new KSButton("Cancel Proposal", new ClickHandler(){
 106  
             public void onClick(ClickEvent event) {
 107  0
                 if (isNew) {
 108  0
                     Application.navigate(AppLocations.Locations.CURRICULUM_MANAGEMENT.getLocation());
 109  
                 } else {
 110  
                     //Cancel the proposal and navigate the user back to curriculum home if cancel was successful.
 111  0
                     workflowUtil.cancel(new Callback<Boolean>() {
 112  
                         @Override
 113  
                         public void exec(Boolean result) {
 114  0
                             if (result) {
 115  0
                                 Application.navigate(AppLocations.Locations.CURRICULUM_MANAGEMENT.getLocation());
 116  
                             }
 117  0
                         }
 118  
 
 119  
                     });
 120  
                 }
 121  0
             }
 122  
         });
 123  
         
 124  0
                 cancelButton.addStyleName("ks-button-spacing");
 125  0
                 cancelButtons.add(cancelButton);
 126  0
                 return cancelButton;
 127  
     }
 128  
         
 129  
         /**
 130  
          * Processes the save, approve, or approve and activate button clicks. The action is determined
 131  
          * by the value of the state parameter.
 132  
          * 
 133  
          * @param state The state to set on the course when saving course data. DRAFT=Save, APPROVED=Approve, and
 134  
          * ACTIVE=Approve & Activate
 135  
          */
 136  
         protected void handleButtonClick(final String state){
 137  
                 
 138  
             //Set state on course before performing save action
 139  0
                 cluProposalModel.set(QueryPath.parse(CreditCourseConstants.STATE), state);
 140  
             
 141  0
             final SaveActionEvent saveActionEvent = getSaveActionEvent(state);
 142  
             
 143  
             //Store the rules if save was called
 144  0
             if((String)cluProposalModel.get(CreditCourseConstants.ID)!=null && cfg instanceof CourseAdminConfigurer){
 145  0
                     ((CourseAdminConfigurer )cfg).getRequisitesSection().storeRules(new Callback<Boolean>(){
 146  
                             public void exec(Boolean result) {
 147  0
                                         if(result){
 148  0
                                                 doAdminSaveAction(saveActionEvent, state);
 149  
                                         }else{
 150  0
                                                 KSNotifier.show("Error saving rules.");
 151  
                                         }
 152  0
                                 }
 153  
                     });
 154  
             }else{
 155  0
             doAdminSaveAction(saveActionEvent, state);                    
 156  
             }
 157  0
         }
 158  
                 
 159  
         /**
 160  
          * Returns a SaveActionEvent with the appropriate ActionCompleteCallback, which will take additional admin actions once
 161  
          * save is complete. The action (i.e. button clicked) is determined by the value of the state parameter 
 162  
          * 
 163  
          * @param state The state to set on the course when saving course data. DRAFT=Save, ACTIVE=Approve & Activate
 164  
          * @return the save event that will be fired based on the button click
 165  
          */
 166  
         private SaveActionEvent getSaveActionEvent(final String state){
 167  0
             final SaveActionEvent saveActionEvent = new SaveActionEvent(false);
 168  0
                 if (DtoConstants.STATE_ACTIVE.equalsIgnoreCase(state)){
 169  0
                     saveActionEvent.setActionCompleteCallback(new ActionCompleteCallback(){
 170  
                                 @Override
 171  
                                 public void onActionComplete(ActionEvent actionEvent) {
 172  0
                                         if (saveActionEvent.isSaveSuccessful()){
 173  0
                                                 workflowUtil.blanketApprove(new Callback<Boolean>(){
 174  
                                                         @Override
 175  
                                                         public void exec(Boolean result) {
 176  
                                                                 
 177  0
                                                                 final ViewContext viewContext = new ViewContext();
 178  0
                                                 viewContext.setId((String)cluProposalModel.get(CreditCourseConstants.ID));
 179  0
                                                 viewContext.setIdType(IdType.OBJECT_ID);                                                                                                                        
 180  0
                                                                 if (DtoConstants.STATE_ACTIVE.equalsIgnoreCase(state)){
 181  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.");
 182  0
                                                                         Application.navigate(AppLocations.Locations.VIEW_COURSE.getLocation(), viewContext);
 183  
                                                                 }
 184  
                                                                 
 185  0
                                                         }
 186  
                                                 });
 187  
                                         }      
 188  0
                                 }
 189  
                     });
 190  
             } else {
 191  
                     //User clicked Save button. When user clicks save, both document upload and cancel should be enabled
 192  0
                     saveActionEvent.setActionCompleteCallback(new ActionCompleteCallback(){
 193  
                                 @Override
 194  
                                 public void onActionComplete(ActionEvent action) {
 195  0
                                         for (KSButton cancelButton:cancelButtons){
 196  0
                                                 cancelButton.setEnabled(true);
 197  0
                                                 ((CourseAdminConfigurer )cfg).getDocumentTool().beforeShow(NO_OP_CALLBACK);
 198  
                                         }                                        
 199  0
                                 }                            
 200  
                     });
 201  
             }
 202  
                 
 203  0
                 return saveActionEvent;
 204  
         }
 205  
         
 206  
         /**
 207  
          * Fires the SaveActionEvent to be handled by the {@link CourseProposalController}. 
 208  
          * 
 209  
          *  @see CourseProposalController#registerModelsAndHandlers()
 210  
          *  @param saveActionEvent SaveActionEvent to fire
 211  
          *  @param state The state to set on the course when saving course data. DRAFT=Save, APPROVED=Approve, and
 212  
          *  ACTIVE=Approve & Activate
 213  
          */
 214  
         private void doAdminSaveAction(final SaveActionEvent saveActionEvent, String state){
 215  0
                 if (DtoConstants.STATE_APPROVED.equalsIgnoreCase(state) || DtoConstants.STATE_ACTIVE.equalsIgnoreCase(state)){
 216  
                         //For Approved action, validate required fields for next (i.e.Approved) state before firing the save action
 217  0
                         cluProposalModel.validateNextState((new Callback<List<ValidationResultInfo>>() {
 218  
                     @Override
 219  
                     public void exec(List<ValidationResultInfo> result) {
 220  
         
 221  0
                             boolean isSectionValid = isValid(result, true);
 222  
         
 223  0
                             if(isSectionValid){
 224  0
                                     CourseAdminController.this.fireApplicationEvent(saveActionEvent);                    }
 225  
                             else{
 226  0
                                     KSNotifier.add(new KSNotification("Unable to save, please check fields for errors.", false, true, 5000));
 227  
                             }
 228  
         
 229  0
                     }
 230  
                 }));
 231  
                 } else {
 232  0
                     CourseAdminController.this.fireApplicationEvent(saveActionEvent);                        
 233  
                 }                 
 234  0
         }
 235  
         
 236  
         /**
 237  
          * 
 238  
          * Override {@link CourseProposalController} because end term should always be editable 
 239  
          * in admin screens.
 240  
          */
 241  
     protected void progressiveEnableFields() {
 242  0
         super.progressiveEnableFields();
 243  0
     }
 244  
         
 245  
         /**
 246  
      * Override the setHeaderTitle to display proper header title for admin screens
 247  
      */
 248  
         @Override
 249  
         protected void setHeaderTitle(){
 250  
             String title;
 251  0
             if (cluProposalModel.get(cfg.getProposalTitlePath()) != null){
 252  0
                     title = getProposalTitle();
 253  
             }
 254  
             else{
 255  0
                     title = "New Course (Admin Proposal)";
 256  
             }
 257  0
             this.setContentTitle(title);
 258  0
             this.setName(title);
 259  0
             WindowTitleUtils.setContextTitle(title);
 260  0
                 currentTitle = title;
 261  0
     }
 262  
     
 263  
         private String getProposalTitle(){
 264  0
                 StringBuffer sb = new StringBuffer();
 265  0
                 sb.append(cluProposalModel.get(cfg.getProposalTitlePath()));
 266  0
                 sb.append(" (Admin Proposal)");
 267  0
                 return sb.toString();
 268  
         }
 269  
         
 270  
         /**
 271  
          * This is a special method for CourseAdminController, which adds a menu item to the navigation menu and navigates
 272  
          * a user to a section within the view rather than a different view.
 273  
          * 
 274  
          * @param parentMenu
 275  
          * @param sectionName
 276  
          * @param sectionId
 277  
          * @param section
 278  
          */
 279  
     public void addMenuItemSection(String parentMenu, final String sectionName, final String widgetId, final Widget widget) {            
 280  0
         KSMenuItemData parentItem = null;
 281  0
         for (int i = 0; i < topLevelMenuItems.size(); i++) {
 282  0
             if (topLevelMenuItems.get(i).getLabel().equals(parentMenu)) {
 283  0
                 parentItem = topLevelMenuItems.get(i);
 284  0
                 break;
 285  
             }
 286  
         }
 287  
 
 288  0
         KSMenuItemData item = new KSMenuItemData(sectionName);
 289  0
             widget.getElement().setId(widgetId);
 290  0
             item.setClickHandler(new ClickHandler(){
 291  
                         @Override
 292  
                 public void onClick(ClickEvent event) {
 293  0
                             Element element = DOM.getElementById(widgetId);
 294  0
                             scrollToSection(element);
 295  0
                         }
 296  
             });
 297  
 
 298  0
         if (parentItem != null) {
 299  0
             parentItem.addSubItem(item);
 300  
         } else {
 301  0
             topLevelMenuItems.add(item);
 302  
         }
 303  
 
 304  0
         menu.refresh();
 305  0
     }
 306  
 
 307  
     public native void scrollToSection(Element element) /*-{
 308  
         element.scrollIntoView();
 309  
     }-*/;
 310  
 
 311  
         @Override
 312  
         protected void configureScreens(DataModelDefinition modelDefinition, final Callback<Boolean> onReadyCallback) {
 313  0
                 super.configureScreens(modelDefinition, previousEndTermConfigurationCallback(onReadyCallback));
 314  0
         }
 315  
 
 316  
         /**
 317  
          * This callback is used to configure the previous end term field after the screens have been configured.
 318  
          * 
 319  
          * @param onReadyCallback
 320  
          * @return
 321  
          */
 322  
         protected Callback<Boolean> previousEndTermConfigurationCallback(final Callback<Boolean> onReadyCallback){
 323  0
                 return new Callback<Boolean>(){
 324  
 
 325  
                         @Override
 326  
                         public void exec(final Boolean result) {
 327  0
                                 requestModel(new ModelRequestCallback<DataModel>(){
 328  
                                         @Override
 329  
                                         public void onModelReady(DataModel model) {
 330  
                                                 //In admin screens add the previous end term field to the active dates section and update it's values   
 331  
                                                 //ONLY when in edit mode. This way it doesn't attempt to retrieve non-existent "Course Info" section;
 332  
                                                 //only the "Summary" section has been configured in non-edit mode.
 333  0
                                                 if (model.getDefinition().getMetadata().isCanEdit()){
 334  0
                                                         String versionedFromId = model.get("versionInfo/versionedFromId");
 335  0
                                                         if(versionedFromId!=null && !versionedFromId.isEmpty()){
 336  
                                                                 //Add the required field 
 337  
                                                                 //See why the required for next state is not set
 338  0
                                                                 VerticalSectionView view = (VerticalSectionView) viewMap.get(CourseSections.COURSE_INFO);                                                        
 339  0
                                                                 Section activeDatesSection = view.getSection(LUUIConstants.ACTIVE_DATES_LABEL_KEY);
 340  0
                                                                 Metadata meta = cfg.getModelDefinition().getMetadata(CourseProposalConfigurer.PROPOSAL_PATH + "/" + CreditCourseConstants.PREV_END_TERM);
 341  0
                                                                 if(meta!=null&&meta.getConstraints().get(0)!=null){
 342  0
                                                                         meta.getConstraints().get(0).setRequiredForNextState(true);
 343  0
                                                                         meta.getConstraints().get(0).setNextState("ACTIVE");
 344  
                                                                 }
 345  0
                                                                 FieldDescriptor fd = cfg.addField(activeDatesSection, CourseProposalConfigurer.PROPOSAL_PATH + "/" + CreditCourseConstants.PREV_END_TERM, cfg.generateMessageInfo(LUUIConstants.PROPOSAL_PREV_END_TERM));
 346  
                                                                 
 347  
                                                                 //FIXME: This static method should not live in WorkflowUtilities
 348  0
                                                                 WorkflowUtilities.updateCrossField(fd, model);
 349  
                                                         }
 350  
                                                 }
 351  
                                                 
 352  0
                                                 onReadyCallback.exec(result);
 353  0
                                         }
 354  
                                         
 355  
                                         @Override
 356  
                                         public void onRequestFail(Throwable cause) {
 357  0
                                                 throw new RuntimeException("Error getting model",cause);
 358  
                                         }
 359  
                                 });
 360  0
                         }
 361  
                 };
 362  
         }
 363  
 
 364  
     @Override
 365  
     public void setViewContext(ViewContext viewContext) {
 366  
         //Determine the permission type being checked
 367  0
         if (viewContext.getId() != null && !viewContext.getId().isEmpty()) {
 368  0
             if (viewContext.getIdType() != IdType.COPY_OF_OBJECT_ID
 369  
                     && viewContext.getIdType() != IdType.COPY_OF_KS_KEW_OBJECT_ID) {
 370  
                 //Id provided, and not a copy id, so opening an existing proposal
 371  0
                 viewContext.setPermissionType(PermissionType.OPEN);
 372  
             } else {
 373  
                 //Copy id provided, so creating a proposal for modification
 374  0
                 viewContext.setPermissionType(PermissionType.INITIATE);
 375  
             }
 376  
         } else {
 377  
             //No id in view context, so creating new empty proposal
 378  0
             viewContext.setPermissionType(PermissionType.INITIATE);
 379  
 
 380  
         }
 381  
         
 382  0
         context = viewContext; 
 383  0
     }
 384  
         
 385  
         /**
 386  
          * This method adds any permission attributes required for checking admin permissions
 387  
          */
 388  
         public void addPermissionAttributes(Map<String, String> attributes){
 389  0
                 super.addPermissionAttributes(attributes);
 390  
                 
 391  0
                 ViewContext viewContext = getViewContext();
 392  
                 
 393  
                 //Determine the permission type being checked
 394  0
             if(viewContext.getId() != null && !viewContext.getId().isEmpty()){
 395  0
                     if(viewContext.getIdType() != IdType.COPY_OF_OBJECT_ID && viewContext.getIdType() != IdType.COPY_OF_KS_KEW_OBJECT_ID){
 396  
                             //Id provided, and not a copy id, so opening an existing proposal
 397  0
                             attributes.put(StudentIdentityConstants.DOCUMENT_TYPE_NAME, LUConstants.PROPOSAL_TYPE_COURSE_CREATE_ADMIN);
 398  
                     } else{
 399  
                             //Copy id provided, so creating a proposal for modification
 400  0
                             attributes.put(StudentIdentityConstants.DOCUMENT_TYPE_NAME, LUConstants.PROPOSAL_TYPE_COURSE_MODIFY_ADMIN);
 401  
                     }
 402  
             } else{
 403  
                     //No id in view context, so creating new empty proposal
 404  0
                         attributes.put(StudentIdentityConstants.DOCUMENT_TYPE_NAME, LUConstants.PROPOSAL_TYPE_COURSE_CREATE_ADMIN);                    
 405  
             }            
 406  0
         }
 407  
         
 408  
 }
 409