Coverage Report - org.kuali.student.lum.lu.ui.course.client.controllers.CourseAdminController
 
Classes in this File Line Coverage Branch Coverage Complexity
CourseAdminController
0%
0/76
0%
0/26
1.929
CourseAdminController$1
0%
0/3
N/A
1.929
CourseAdminController$2
0%
0/3
N/A
1.929
CourseAdminController$3
0%
0/5
0%
0/2
1.929
CourseAdminController$3$1
0%
0/4
0%
0/2
1.929
CourseAdminController$4
0%
0/5
0%
0/2
1.929
CourseAdminController$5
0%
0/4
0%
0/2
1.929
CourseAdminController$5$1
0%
0/8
0%
0/2
1.929
CourseAdminController$6
0%
0/5
0%
0/2
1.929
CourseAdminController$7
0%
0/6
0%
0/2
1.929
CourseAdminController$8
0%
0/4
N/A
1.929
CourseAdminController$9
0%
0/3
N/A
1.929
CourseAdminController$9$1
0%
0/15
0%
0/10
1.929
 
 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  0
     }
 78  
 
 79  
         /**
 80  
          * Override the getSaveButton to provide a new set of buttons for the admin screens
 81  
          */
 82  
         @Override
 83  
         public KSButton getSaveButton(){
 84  0
                 KSButton saveButton = new KSButton("Save", new ClickHandler(){
 85  
             public void onClick(ClickEvent event) {
 86  0
                     handleButtonClick(DtoConstants.STATE_DRAFT);                    
 87  0
             }
 88  
         });                
 89  
                 
 90  0
                 saveButton.addStyleName("ks-button-spacing");
 91  0
                 return saveButton;
 92  
         }
 93  
                 
 94  
         public KSButton getApproveAndActivateButton(){
 95  0
                 return new KSButton("Approve and Activate", new ClickHandler(){
 96  
             public void onClick(ClickEvent event) {
 97  0
                     handleButtonClick(DtoConstants.STATE_ACTIVE);
 98  0
             }
 99  
         });                
 100  
         }
 101  
 
 102  
         public KSButton getCancelButton(){
 103  0
                 KSButton cancelButton = new KSButton("Cancel Proposal", new ClickHandler(){
 104  
             public void onClick(ClickEvent event) {
 105  0
                 if (isNew) {
 106  0
                     Application.navigate(AppLocations.Locations.CURRICULUM_MANAGEMENT.getLocation());
 107  
                 } else {
 108  
                     //Cancel the proposal and navigate the user back to curriculum home if cancel was successful.
 109  0
                     workflowUtil.cancel(new Callback<Boolean>() {
 110  
                         @Override
 111  
                         public void exec(Boolean result) {
 112  0
                             if (result) {
 113  0
                                 Application.navigate(AppLocations.Locations.CURRICULUM_MANAGEMENT.getLocation());
 114  
                             }
 115  0
                         }
 116  
 
 117  
                     });
 118  
                 }
 119  0
             }
 120  
         });
 121  
         
 122  0
                 cancelButton.addStyleName("ks-button-spacing");
 123  0
                 cancelButtons.add(cancelButton);
 124  0
                 return cancelButton;
 125  
     }
 126  
         
 127  
         /**
 128  
          * Processes the save, approve, or approve and activate button clicks. The action is determined
 129  
          * by the value of the state parameter.
 130  
          * 
 131  
          * @param state The state to set on the course when saving course data. DRAFT=Save, APPROVED=Approve, and
 132  
          * ACTIVE=Approve & Activate
 133  
          */
 134  
         protected void handleButtonClick(final String state){
 135  
                 
 136  
             //Set state on course before performing save action
 137  0
                 cluProposalModel.set(QueryPath.parse(CreditCourseConstants.STATE), state);
 138  
             
 139  0
             final SaveActionEvent saveActionEvent = getSaveActionEvent(state);
 140  
             
 141  
             //Store the rules if save was called
 142  0
             if((String)cluProposalModel.get(CreditCourseConstants.ID)!=null && cfg instanceof CourseAdminConfigurer){
 143  0
                     ((CourseAdminConfigurer )cfg).getRequisitesSection().storeRules(new Callback<Boolean>(){
 144  
                             public void exec(Boolean result) {
 145  0
                                         if(result){
 146  0
                                                 doAdminSaveAction(saveActionEvent, state);
 147  
                                         }else{
 148  0
                                                 KSNotifier.show("Error saving rules.");
 149  
                                         }
 150  0
                                 }
 151  
                     });
 152  
             }else{
 153  0
             doAdminSaveAction(saveActionEvent, state);                    
 154  
             }
 155  0
         }
 156  
                 
 157  
         /**
 158  
          * Returns a SaveActionEvent with the appropriate ActionCompleteCallback, which will take additional admin actions once
 159  
          * save is complete. The action (i.e. button clicked) is determined by the value of the state parameter 
 160  
          * 
 161  
          * @param state The state to set on the course when saving course data. DRAFT=Save, 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_ACTIVE.equalsIgnoreCase(state)){
 167  0
                     saveActionEvent.setActionCompleteCallback(new ActionCompleteCallback(){
 168  
                                 @Override
 169  
                                 public void onActionComplete(ActionEvent actionEvent) {
 170  0
                                         if (saveActionEvent.isSaveSuccessful()){
 171  0
                                                 workflowUtil.blanketApprove(new Callback<Boolean>(){
 172  
                                                         @Override
 173  
                                                         public void exec(Boolean result) {
 174  
                                                                 
 175  0
                                                                 final ViewContext viewContext = new ViewContext();
 176  0
                                                 viewContext.setId((String)cluProposalModel.get(CreditCourseConstants.ID));
 177  0
                                                 viewContext.setIdType(IdType.OBJECT_ID);                                                                                                                        
 178  0
                                                                 if (DtoConstants.STATE_ACTIVE.equalsIgnoreCase(state)){
 179  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.");
 180  0
                                                                         Application.navigate(AppLocations.Locations.VIEW_COURSE.getLocation(), viewContext);
 181  
                                                                 }
 182  
                                                                 
 183  0
                                                         }
 184  
                                                 });
 185  
                                         }      
 186  0
                                 }
 187  
                     });
 188  
             } else {
 189  
                     //User clicked Save button. When user clicks save, both document upload and cancel should be enabled
 190  0
                     saveActionEvent.setActionCompleteCallback(new ActionCompleteCallback(){
 191  
                                 @Override
 192  
                                 public void onActionComplete(ActionEvent action) {
 193  0
                                         for (KSButton cancelButton:cancelButtons){
 194  0
                                                 cancelButton.setEnabled(true);
 195  0
                                                 ((CourseAdminConfigurer )cfg).getDocumentTool().beforeShow(NO_OP_CALLBACK);
 196  
                                         }                                        
 197  0
                                 }                            
 198  
                     });
 199  
             }
 200  
                 
 201  0
                 return saveActionEvent;
 202  
         }
 203  
         
 204  
         /**
 205  
          * Fires the SaveActionEvent to be handled by the {@link CourseProposalController}. 
 206  
          * 
 207  
          *  @see CourseProposalController#registerModelsAndHandlers()
 208  
          *  @param saveActionEvent SaveActionEvent to fire
 209  
          *  @param state The state to set on the course when saving course data. DRAFT=Save, APPROVED=Approve, and
 210  
          *  ACTIVE=Approve & Activate
 211  
          */
 212  
         private void doAdminSaveAction(final SaveActionEvent saveActionEvent, String state){
 213  0
                 if (DtoConstants.STATE_APPROVED.equalsIgnoreCase(state) || DtoConstants.STATE_ACTIVE.equalsIgnoreCase(state)){
 214  
                         //For Approved action, validate required fields for next (i.e.Approved) state before firing the save action
 215  0
                         cluProposalModel.validateNextState((new Callback<List<ValidationResultInfo>>() {
 216  
                     @Override
 217  
                     public void exec(List<ValidationResultInfo> result) {
 218  
         
 219  0
                             boolean isSectionValid = isValid(result, true);
 220  
         
 221  0
                             if(isSectionValid){
 222  0
                                     CourseAdminController.this.fireApplicationEvent(saveActionEvent);                    }
 223  
                             else{
 224  0
                                     KSNotifier.add(new KSNotification("Unable to save, please check fields for errors.", false, true, 5000));
 225  
                             }
 226  
         
 227  0
                     }
 228  
                 }));
 229  
                 } else {
 230  0
                     CourseAdminController.this.fireApplicationEvent(saveActionEvent);                        
 231  
                 }                 
 232  0
         }
 233  
         
 234  
         /**
 235  
          * 
 236  
          * Override {@link CourseProposalController} because end term should always be editable 
 237  
          * in admin screens.
 238  
          */
 239  
     protected void progressiveEnableFields() {
 240  0
         super.progressiveEnableFields();
 241  0
     }
 242  
         
 243  
         /**
 244  
      * Override the setHeaderTitle to display proper header title for admin screens
 245  
      */
 246  
         @Override
 247  
         protected void setHeaderTitle(){
 248  
             String title;
 249  0
             if (cluProposalModel.get(cfg.getProposalTitlePath()) != null){
 250  0
                     title = getProposalTitle();
 251  
             }
 252  
             else{
 253  0
                     title = "New Course (Admin Proposal)";
 254  
             }
 255  0
             this.setContentTitle(title);
 256  0
             this.setName(title);
 257  0
             WindowTitleUtils.setContextTitle(title);
 258  0
                 currentTitle = title;
 259  0
     }
 260  
     
 261  
         private String getProposalTitle(){
 262  0
                 StringBuffer sb = new StringBuffer();
 263  0
                 sb.append(cluProposalModel.get(cfg.getProposalTitlePath()));
 264  0
                 sb.append(" (Admin Proposal)");
 265  0
                 return sb.toString();
 266  
         }
 267  
         
 268  
         /**
 269  
          * This is a special method for CourseAdminController, which adds a menu item to the navigation menu and navigates
 270  
          * a user to a section within the view rather than a different view.
 271  
          * 
 272  
          * @param parentMenu
 273  
          * @param sectionName
 274  
          * @param sectionId
 275  
          * @param section
 276  
          */
 277  
     public void addMenuItemSection(String parentMenu, final String sectionName, final String widgetId, final Widget widget) {            
 278  0
         KSMenuItemData parentItem = null;
 279  0
         for (int i = 0; i < topLevelMenuItems.size(); i++) {
 280  0
             if (topLevelMenuItems.get(i).getLabel().equals(parentMenu)) {
 281  0
                 parentItem = topLevelMenuItems.get(i);
 282  0
                 break;
 283  
             }
 284  
         }
 285  
 
 286  0
         KSMenuItemData item = new KSMenuItemData(sectionName);
 287  0
             widget.getElement().setId(widgetId);
 288  0
             item.setClickHandler(new ClickHandler(){
 289  
                         @Override
 290  
                 public void onClick(ClickEvent event) {
 291  0
                             Element element = DOM.getElementById(widgetId);
 292  0
                             scrollToSection(element);
 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  
     public native void scrollToSection(Element element) /*-{
 306  
         element.scrollIntoView();
 307  
     }-*/;
 308  
 
 309  
         @Override
 310  
         protected void configureScreens(DataModelDefinition modelDefinition, final Callback<Boolean> onReadyCallback) {
 311  0
                 super.configureScreens(modelDefinition, previousEndTermConfigurationCallback(onReadyCallback));
 312  0
         }
 313  
 
 314  
         /**
 315  
          * This callback is used to configure the previous end term field after the screens have been configured.
 316  
          * 
 317  
          * @param onReadyCallback
 318  
          * @return
 319  
          */
 320  
         protected Callback<Boolean> previousEndTermConfigurationCallback(final Callback<Boolean> onReadyCallback){
 321  0
                 return new Callback<Boolean>(){
 322  
 
 323  
                         @Override
 324  
                         public void exec(final Boolean result) {
 325  0
                                 requestModel(new ModelRequestCallback<DataModel>(){
 326  
                                         @Override
 327  
                                         public void onModelReady(DataModel model) {
 328  
                                                 //In admin screens add the previous end term field to the active dates section and update it's values   
 329  
                                                 //ONLY when in edit mode. This way it doesn't attempt to retrieve non-existent "Course Info" section;
 330  
                                                 //only the "Summary" section has been configured in non-edit mode.
 331  0
                                                 if (model.getDefinition().getMetadata().isCanEdit()){
 332  0
                                                         String versionedFromId = model.get("versionInfo/versionedFromId");
 333  0
                                                         if(versionedFromId!=null && !versionedFromId.isEmpty()){
 334  
                                                                 //Add the required field 
 335  
                                                                 //See why the required for next state is not set
 336  0
                                                                 VerticalSectionView view = (VerticalSectionView) viewMap.get(CourseSections.COURSE_INFO);                                                        
 337  0
                                                                 Section activeDatesSection = view.getSection(LUUIConstants.ACTIVE_DATES_LABEL_KEY);
 338  0
                                                                 Metadata meta = cfg.getModelDefinition().getMetadata(CourseProposalConfigurer.PROPOSAL_PATH + "/" + CreditCourseConstants.PREV_END_TERM);
 339  0
                                                                 if(meta!=null&&meta.getConstraints().get(0)!=null){
 340  0
                                                                         meta.getConstraints().get(0).setRequiredForNextState(true);
 341  0
                                                                         meta.getConstraints().get(0).setNextState("ACTIVE");
 342  
                                                                 }
 343  0
                                                                 FieldDescriptor fd = cfg.addField(activeDatesSection, CourseProposalConfigurer.PROPOSAL_PATH + "/" + CreditCourseConstants.PREV_END_TERM, cfg.generateMessageInfo(LUUIConstants.PROPOSAL_PREV_END_TERM));
 344  
                                                                 
 345  
                                                                 //FIXME: This static method should not live in WorkflowUtilities
 346  0
                                                                 WorkflowUtilities.updateCrossField(fd, model);
 347  
                                                         }
 348  
                                                 }
 349  
                                                 
 350  0
                                                 onReadyCallback.exec(result);
 351  0
                                         }
 352  
                                         
 353  
                                         @Override
 354  
                                         public void onRequestFail(Throwable cause) {
 355  0
                                                 throw new RuntimeException("Error getting model",cause);
 356  
                                         }
 357  
                                 });
 358  0
                         }
 359  
                 };
 360  
         }
 361  
         
 362  
         /**
 363  
          * This method adds any permission attributes required for checking admin permissions
 364  
          */
 365  
         public void addPermissionAttributes(Map<String, String> attributes){
 366  0
                 super.addPermissionAttributes(attributes);
 367  
                 
 368  0
                 ViewContext viewContext = getViewContext();
 369  
                 
 370  
                 //Determine the permission type being checked
 371  0
             if(viewContext.getId() != null && !viewContext.getId().isEmpty()){
 372  0
                     if(viewContext.getIdType() != IdType.COPY_OF_OBJECT_ID && viewContext.getIdType() != IdType.COPY_OF_KS_KEW_OBJECT_ID){
 373  
                             //Id provided, and not a copy id, so opening an existing proposal
 374  0
                             viewContext.setPermissionType(PermissionType.OPEN);
 375  0
                             attributes.put(StudentIdentityConstants.DOCUMENT_TYPE_NAME, LUConstants.PROPOSAL_TYPE_COURSE_CREATE_ADMIN);
 376  
                     } else{
 377  
                             //Copy id provided, so creating a proposal for modification
 378  0
                             viewContext.setPermissionType(PermissionType.INITIATE);
 379  0
                             attributes.put(StudentIdentityConstants.DOCUMENT_TYPE_NAME, LUConstants.PROPOSAL_TYPE_COURSE_MODIFY_ADMIN);
 380  
                     }
 381  
             } else{
 382  
                     //No id in view context, so creating new empty proposal
 383  0
                     viewContext.setPermissionType(PermissionType.INITIATE);
 384  0
                         attributes.put(StudentIdentityConstants.DOCUMENT_TYPE_NAME, LUConstants.PROPOSAL_TYPE_COURSE_CREATE_ADMIN);                    
 385  
             }            
 386  0
         }
 387  
         
 388  
 }
 389