View Javadoc

1   /*
2    * Copyright 2011 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 1.0 (the
5    * "License"); you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl1.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package org.kuali.student.enrollment.class2.acal.controller;
17  
18  import org.apache.commons.lang.BooleanUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.log4j.Logger;
21  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
22  import org.kuali.rice.core.api.util.KeyValue;
23  import org.kuali.rice.core.api.util.RiceKeyConstants;
24  import org.kuali.rice.krad.uif.UifParameters;
25  import org.kuali.rice.krad.uif.component.Component;
26  import org.kuali.rice.krad.uif.control.SelectControl;
27  import org.kuali.rice.krad.uif.util.ComponentFactory;
28  import org.kuali.rice.krad.uif.util.UifKeyValue;
29  import org.kuali.rice.krad.uif.view.DialogManager;
30  import org.kuali.rice.krad.util.GlobalVariables;
31  import org.kuali.rice.krad.util.KRADConstants;
32  import org.kuali.rice.krad.web.controller.UifControllerBase;
33  import org.kuali.rice.krad.web.form.UifFormBase;
34  import org.kuali.student.common.uif.util.KSControllerHelper;
35  import org.kuali.student.enrollment.class2.acal.dto.AcademicTermWrapper;
36  import org.kuali.student.enrollment.class2.acal.dto.AcalEventWrapper;
37  import org.kuali.student.enrollment.class2.acal.dto.ExamPeriodWrapper;
38  import org.kuali.student.enrollment.class2.acal.dto.HolidayCalendarWrapper;
39  import org.kuali.student.enrollment.class2.acal.dto.HolidayWrapper;
40  import org.kuali.student.enrollment.class2.acal.dto.KeyDateWrapper;
41  import org.kuali.student.enrollment.class2.acal.dto.KeyDatesGroupWrapper;
42  import org.kuali.student.enrollment.class2.acal.form.AcademicCalendarForm;
43  import org.kuali.student.enrollment.class2.acal.service.AcademicCalendarViewHelperService;
44  import org.kuali.student.enrollment.class2.acal.util.CalendarConstants;
45  import org.kuali.student.enrollment.class2.acal.util.AcalCommonUtils;
46  import org.kuali.student.enrollment.common.util.EnrollConstants;
47  import org.kuali.student.r2.common.dto.AttributeInfo;
48  import org.kuali.student.r2.common.dto.RichTextInfo;
49  import org.kuali.student.r2.common.dto.StatusInfo;
50  import org.kuali.student.r2.common.exceptions.OperationFailedException;
51  import org.kuali.student.r2.common.exceptions.VersionMismatchException;
52  import org.kuali.student.r2.core.acal.dto.AcademicCalendarInfo;
53  import org.kuali.student.r2.core.acal.dto.AcalEventInfo;
54  import org.kuali.student.r2.core.acal.dto.ExamPeriodInfo;
55  import org.kuali.student.r2.core.acal.dto.HolidayInfo;
56  import org.kuali.student.r2.core.acal.dto.KeyDateInfo;
57  import org.kuali.student.r2.core.acal.dto.TermInfo;
58  import org.kuali.student.r2.core.acal.service.AcademicCalendarService;
59  import org.kuali.student.r2.core.acal.service.facade.AcademicCalendarServiceFacade;
60  import org.kuali.student.r2.core.constants.AcademicCalendarServiceConstants;
61  import org.kuali.student.r2.core.constants.AtpServiceConstants;
62  import org.springframework.stereotype.Controller;
63  import org.springframework.validation.BindingResult;
64  import org.springframework.web.bind.annotation.ModelAttribute;
65  import org.springframework.web.bind.annotation.RequestMapping;
66  import org.springframework.web.bind.annotation.RequestMethod;
67  import org.springframework.web.servlet.ModelAndView;
68  
69  import javax.servlet.http.HttpServletRequest;
70  import javax.servlet.http.HttpServletResponse;
71  import javax.xml.namespace.QName;
72  import java.lang.reflect.InvocationTargetException;
73  import java.lang.reflect.Method;
74  import java.util.ArrayList;
75  import java.util.Date;
76  import java.util.List;
77  import java.util.Properties;
78  
79  /**
80   * This controller handles all the request from Academic calendar UI.
81   *
82   * These are the related xmls from where requests come to this controller
83   * 1. AcademicCalendarView.xml
84   * 2. AcademicCalendarEditPage.xml
85   * 3. AcademicCalendarCopyPage.xml
86   * 4. AcademicTermPage.xml
87   *
88   * @author Kuali Student Team
89   */
90  
91  @Controller
92  @RequestMapping(value = "/academicCalendar")
93  public class AcademicCalendarController extends UifControllerBase {
94  
95      private static final Logger LOG = Logger.getLogger(AcademicCalendarController.class);
96  
97      private AcademicCalendarService acalService;
98      private AcademicCalendarServiceFacade academicCalendarServiceFacade;
99  
100     @Override
101     protected UifFormBase createInitialForm(HttpServletRequest request) {
102         return new AcademicCalendarForm();
103     }
104 
105     /**
106      * This method loads an academic calendar based on the parameters passed into the request.
107      *
108      * These are the supported request parameters (specific to Acal)
109      * 1. id - Academic Calendar Id to load in to UI
110      * 2. readOnlyView - If true, sets the view as read only
111      * 3. selectTab - can be 'info' or 'term'
112      *
113      */
114     @Override
115     @RequestMapping(method = RequestMethod.GET, params = "methodToCall=start")
116     public ModelAndView start(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
117                               HttpServletRequest request, HttpServletResponse response) {
118         AcademicCalendarForm acalForm = (AcademicCalendarForm) form;
119 
120         String acalId = request.getParameter(CalendarConstants.CALENDAR_ID);
121 
122         if (StringUtils.isNotBlank(acalId)){
123             getAcalViewHelperService(acalForm).populateAcademicCalendar(acalId, acalForm);
124         }
125 
126         String readOnlyView = request.getParameter(CalendarConstants.READ_ONLY_VIEW);
127         acalForm.getView().setReadOnly(BooleanUtils.toBoolean(readOnlyView));
128 
129         if (StringUtils.isNotBlank(request.getParameter(CalendarConstants.SELECT_TAB))) {
130             acalForm.setDefaultTabToShow(request.getParameter(CalendarConstants.SELECT_TAB));
131         }
132 
133         return getUIFModelAndView(form);
134     }
135 
136     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=reload")
137     public ModelAndView reload(@ModelAttribute("KualiForm") AcademicCalendarForm acalForm, BindingResult result,
138                                HttpServletRequest request, HttpServletResponse response) {
139         String acalId = acalForm.getAcademicCalendarInfo().getId();
140         getAcalViewHelperService(acalForm).populateAcademicCalendar(acalId, acalForm);
141         acalForm.setReload(false);
142         return getUIFModelAndView(acalForm, CalendarConstants.ACADEMIC_CALENDAR_EDIT_PAGE);
143     }
144 
145     /**
146      * This method creates a blank academic calendar.
147      */
148     @RequestMapping(params = "methodToCall=createBlankCalendar")
149     public ModelAndView createBlankCalendar(@ModelAttribute("KualiForm") AcademicCalendarForm acalForm, BindingResult result,
150                                             HttpServletRequest request, HttpServletResponse response){
151 
152         acalForm.setAcademicCalendarInfo(new AcademicCalendarInfo());
153         acalForm.setEvents(new ArrayList<AcalEventWrapper>());
154         acalForm.setHolidayCalendarList(new ArrayList<HolidayCalendarWrapper>());
155         acalForm.setTermWrapperList(new ArrayList<AcademicTermWrapper>());
156 
157         return getUIFModelAndView(acalForm, CalendarConstants.ACADEMIC_CALENDAR_EDIT_PAGE);
158     }
159 
160     /**
161      * This method is called before the Create New Academic Calendar page is displayed
162      *
163      * It fills in the original Acal for the form with the latest calendar found, by default
164      */
165     @RequestMapping(method = RequestMethod.GET, params = "methodToCall=startNew")
166     public ModelAndView startNew( @ModelAttribute("KualiForm") AcademicCalendarForm acalForm, BindingResult result,
167                                   HttpServletRequest request, HttpServletResponse response) {
168 
169         try {
170             AcademicCalendarInfo acalInfo = getAcalViewHelperService(acalForm).getLatestAcademicCalendar();
171             acalForm.setCopyFromAcal(acalInfo);
172             acalForm.setMeta(acalInfo.getMeta());
173         } catch (Exception e) {
174             throw getAcalViewHelperService(acalForm).convertServiceExceptionsToUI(e);
175         }
176 
177         return super.start(acalForm, result, request, response);
178     }
179 
180     /**
181      * This method will be called when user clicks on the edit link at the header portion of acal.
182      *
183      * @param acalForm
184      * @param result
185      * @param request
186      * @param response
187      * @return
188      */
189     @RequestMapping(params = "methodToCall=toEdit")
190     public ModelAndView toEdit(@ModelAttribute("KualiForm") AcademicCalendarForm acalForm, BindingResult result,
191                                HttpServletRequest request, HttpServletResponse response){
192         AcademicCalendarInfo acalInfo = acalForm.getAcademicCalendarInfo();
193         AcademicCalendarInfo orgAcalInfo = acalForm.getCopyFromAcal();
194 
195         if (StringUtils.isBlank(acalInfo.getId()) && StringUtils.isNotBlank(orgAcalInfo.getId())){
196             getAcalViewHelperService(acalForm).populateAcademicCalendar(orgAcalInfo.getId(), acalForm);
197             acalForm.setCopyFromAcal(new AcademicCalendarInfo());
198         }
199 
200         acalForm.getView().setReadOnly(false);
201 
202         return getUIFModelAndView(acalForm, CalendarConstants.ACADEMIC_CALENDAR_EDIT_PAGE);
203     }
204 
205     /**
206      * Passes on request from Acal view to copy the Acal to the copyForNew method.  Needed a forwarder here because the
207      * call is a GET rather than a POST
208      *
209      * @param acalForm
210      * @param result
211      * @param request
212      * @param response
213      * @return
214      */
215     @RequestMapping(params = "methodToCall=toCopy")
216     public ModelAndView toCopy(@ModelAttribute("KualiForm") AcademicCalendarForm acalForm, BindingResult result,
217                                HttpServletRequest request, HttpServletResponse response){
218 
219         AcademicCalendarInfo acalInfo = null;
220 
221         String acalId = request.getParameter(CalendarConstants.CALENDAR_ID);
222         if (acalId != null && !acalId.trim().isEmpty()) {
223             String pageId = request.getParameter(CalendarConstants.PAGE_ID);
224             if (CalendarConstants.ACADEMIC_CALENDAR_COPY_PAGE.equals(pageId)) {
225 
226                 try {
227                     acalInfo= getAcalService().getAcademicCalendar(acalId, getAcalViewHelperService(acalForm).createContextInfo());
228                     acalForm.setCopyFromAcal(acalInfo);
229                 } catch (Exception ex) {
230                     throw getAcalViewHelperService(acalForm).convertServiceExceptionsToUI(ex);
231                 }
232             }
233         }
234         else {
235             // try to get the AC from the form (in case of copy from the Acal view page) and set it as the Original Acal
236             try {
237                 acalInfo = acalForm.getAcademicCalendarInfo();
238                 acalForm.reset();
239                 acalForm.setNewCalendar(true);
240                 acalForm.setCopyFromAcal(acalInfo);
241             }
242             catch (Exception e) {
243                 throw getAcalViewHelperService(acalForm).convertServiceExceptionsToUI(e);
244             }
245 
246         }
247 
248         // Anything that extends KSUIFForm.java will have meta information
249         if(acalInfo != null){
250             acalForm.setMeta(acalInfo.getMeta());
251         }
252 
253         return copy(acalForm, result, request, response);
254     }
255 
256     /**
257      * Copy over from the existing AcalInfo to create a new.
258      *
259      * @param acalForm
260      * @param result
261      * @param request
262      * @param response
263      * @return
264      */
265     @RequestMapping(method = RequestMethod.POST, params="methodToCall=copy")
266     public ModelAndView copy( @ModelAttribute("KualiForm") AcademicCalendarForm acalForm, BindingResult result,
267                               HttpServletRequest request, HttpServletResponse response) {
268 
269         getAcalViewHelperService(acalForm).copyToCreateAcademicCalendar(acalForm);
270 
271         return getUIFModelAndView(acalForm, CalendarConstants.ACADEMIC_CALENDAR_EDIT_PAGE);
272 
273     }
274 
275     //Editing Hcal is implemented fully and it's working.. but we're having some issues with KRAD form management
276     //This will be implemented in future milestones
277     /*@RequestMapping(method = RequestMethod.POST, params="methodToCall=editHolidayCalendar")
278     public ModelAndView editHolidayCalendar( @ModelAttribute("KualiForm") AcademicCalendarForm acalForm, BindingResult result,
279                                             HttpServletRequest request, HttpServletResponse response) {
280 
281         String selectedCollectionPath = acalForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH);
282         if (StringUtils.isBlank(selectedCollectionPath)) {
283             throw new RuntimeException("unable to determine the selected collection path");
284         }
285 
286         int selectedLineIndex = -1;
287         String selectedLine = acalForm.getActionParamaterValue(UifParameters.SELECTED_LINE_INDEX);
288         if (StringUtils.isNotBlank(selectedLine)) {
289             selectedLineIndex = Integer.parseInt(selectedLine);
290         }
291 
292         if (selectedLineIndex == -1) {
293             throw new RuntimeException("unable to determine the selected line index");
294         }
295 
296         Properties hcalURLParam = new Properties();
297         hcalURLParam.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, CalendarConstants.HC_EDIT_METHOD);
298         hcalURLParam.put(CalendarConstants.CALENDAR_ID, acalForm.getHolidayCalendarList().get(selectedLineIndex).getId());
299         hcalURLParam.put(UifParameters.VIEW_ID, CalendarConstants.HOLIDAYCALENDAR_FLOWVIEW);
300         hcalURLParam.put(UifParameters.PAGE_ID, CalendarConstants.HOLIDAYCALENDAR_EDITPAGE);
301         hcalURLParam.put(UifParameters.RETURN_FORM_KEY, acalForm.getFormKey());
302 
303         Properties acalReturnURLParams = new Properties();
304         acalReturnURLParams.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, CalendarConstants.AC_EDIT_METHOD);
305         acalReturnURLParams.put(UifParameters.VIEW_ID, CalendarConstants.ACAL_VIEW);
306         acalReturnURLParams.put(UifParameters.PAGE_ID, CalendarConstants.ACADEMIC_CALENDAR_EDIT_PAGE);
307         String returnUrl = UrlFactory.parameterizeUrl(CalendarConstants.ACAL_CONTROLLER_PATH, acalReturnURLParams);
308         hcalURLParam.put(UifParameters.RETURN_LOCATION, returnUrl);
309 
310         return super.performRedirect(acalForm,CalendarConstants.HCAL_CONTROLLER_PATH, hcalURLParam);
311 
312     }*/
313 
314 
315     /**
316      * Redirects the user to the search Calendar page
317      *
318      * @param acalForm
319      * @param result
320      * @param request
321      * @param response
322      * @return
323      */
324     @RequestMapping(params = "methodToCall=search")
325     public ModelAndView search(@ModelAttribute("KualiForm") AcademicCalendarForm acalForm, BindingResult result,
326                                HttpServletRequest request, HttpServletResponse response) {
327 
328         String controllerPath = CalendarConstants.CALENDAR_SEARCH_CONTROLLER_PATH;
329         Properties urlParameters = new Properties();
330         urlParameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, KRADConstants.START_METHOD);
331         urlParameters.put(UifParameters.VIEW_ID, CalendarConstants.CALENDAR_SEARCH_VIEW);
332         // UrlParams.SHOW_HISTORY and SHOW_HOME no longer exist
333         // https://fisheye.kuali.org/changelog/rice?cs=39034
334         // TODO KSENROLL-8469
335         //urlParameters.put(UifConstants.UrlParams.SHOW_HISTORY, BooleanUtils.toStringTrueFalse(false));
336         return super.performRedirect(acalForm,controllerPath, urlParameters);
337     }
338 
339     /**
340      * This will save the academic calendar and terms.  Uses dialog confirmations to verify changes from draft to
341      * offical state.
342      *
343      * @param academicCalendarForm
344      * @param result
345      * @param request
346      * @param response
347      * @return
348      */
349     @RequestMapping(params = "methodToCall=save")
350     public ModelAndView save(@ModelAttribute("KualiForm") AcademicCalendarForm academicCalendarForm, BindingResult result,
351                              HttpServletRequest request, HttpServletResponse response) {
352         AcademicCalendarForm acal = saveAcademicCalendarDirtyFields(academicCalendarForm);
353         return getUIFModelAndView(acal);
354     }
355 
356     /**
357      * Method used to delete AcademicCalendar
358      *
359      * @param acalForm
360      * @param result
361      * @param request
362      * @param response
363      * @return
364      */
365     @RequestMapping(params = "methodToCall=delete")
366     public ModelAndView delete(@ModelAttribute("KualiForm") AcademicCalendarForm acalForm, BindingResult result,
367                                HttpServletRequest request, HttpServletResponse response) {
368         String dialog = CalendarConstants.ACADEMIC_DELETE_CONFIRMATION_DIALOG;
369         if (!hasDialogBeenDisplayed(dialog, acalForm)) {
370 
371             //redirect back to client to display lightbox
372             return showDialog(dialog, acalForm, request, response);
373         }else{
374             if(hasDialogBeenAnswered(dialog,acalForm)){
375                 boolean confirmDelete = getBooleanDialogResponse(dialog, acalForm, request, response);
376                 acalForm.getDialogManager().resetDialogStatus(dialog);
377                 if(!confirmDelete){
378                     return getUIFModelAndView(acalForm);
379                 }
380             } else {
381 
382                 //redirect back to client to display lightbox
383                 return showDialog(dialog, acalForm, request, response);
384             }
385         }
386 
387         StatusInfo statusInfo;
388         try {
389             statusInfo = getAcademicCalendarServiceFacade().deleteCalendarCascaded(acalForm.getAcademicCalendarInfo().getId(), getAcalViewHelperService(acalForm).createContextInfo());
390         } catch (Exception e) {
391             throw getAcalViewHelperService(acalForm).convertServiceExceptionsToUI(e);
392         }
393 
394         if (statusInfo.getIsSuccess()){
395             // If delete successful, populate growl message parameters and redirect to the search page.
396             Properties urlParameters = new Properties();
397             urlParameters.put(EnrollConstants.GROWL_TITLE,"");
398             urlParameters.put(EnrollConstants.GROWL_MESSAGE, CalendarConstants.MessageKeys.INFO_ACADEMIC_CALENDAR_DELETED);
399             urlParameters.put(EnrollConstants.GROWL_MESSAGE_PARAMS,acalForm.getAcademicCalendarInfo().getName());
400             return redirectToSearch(acalForm,request,urlParameters);
401         } else {
402             GlobalVariables.getMessageMap().putInfo(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_DELETING, acalForm.getAcademicCalendarInfo().getName(),statusInfo.getMessage());
403             return getUIFModelAndView(acalForm);
404         }
405 
406 
407     }
408 
409     /**
410      * Cancel editing a term. This will undo all the changes done by the users. Basically, it loads the term info
411      * from the DB.
412      *
413      * @param academicCalendarForm
414      * @param result
415      * @param request
416      * @param response
417      * @return
418      */
419     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=cancelTerm")
420     public ModelAndView cancelTerm(@ModelAttribute("KualiForm") AcademicCalendarForm academicCalendarForm, BindingResult result,
421                                    HttpServletRequest request, HttpServletResponse response) {
422         academicCalendarForm.setFieldsToSave(processDirtyFields(academicCalendarForm));
423         int selectedLineIndex = KSControllerHelper.getSelectedCollectionLineIndex(academicCalendarForm);
424 
425         AcademicTermWrapper termWrapper = academicCalendarForm.getTermWrapperList().get(selectedLineIndex);
426 
427         AcademicCalendarViewHelperService viewHelperService = getAcalViewHelperService(academicCalendarForm);
428 
429         if (termWrapper.isNew()){
430             academicCalendarForm.getTermWrapperList().remove(selectedLineIndex);
431             if (termWrapper.isHasSubterm()){
432                 List<AcademicTermWrapper> subTerms = termWrapper.getSubterms();
433                 for(AcademicTermWrapper subTerm : subTerms){
434                     academicCalendarForm.getTermWrapperList().remove(subTerm);
435                 }
436             }
437         }else{  //this part will not be called since cancel Term link does not show up after a term is persisted in DB.
438             try {
439                 TermInfo termInfo = getAcalService().getTerm(termWrapper.getTermInfo().getId(), viewHelperService.createContextInfo());
440                 boolean calculateInstrDays = !academicCalendarForm.getHolidayCalendarList().isEmpty();
441                 AcademicTermWrapper termWrapperFromDB = viewHelperService.populateTermWrapper(termInfo, false,calculateInstrDays);
442                 academicCalendarForm.getTermWrapperList().set(selectedLineIndex,termWrapperFromDB);
443             } catch (Exception e) {
444                 throw getAcalViewHelperService(academicCalendarForm).convertServiceExceptionsToUI(e);
445             }
446         }
447 
448         return getUIFModelAndView(academicCalendarForm);
449     }
450 
451     /**
452      * This method makes a term official and persist to DB. User can selectively make a term official.
453      *
454      * @param academicCalendarForm
455      * @param result
456      * @param request
457      * @param response
458      * @return
459      */
460     @RequestMapping(params = "methodToCall=makeTermOfficial")
461     public ModelAndView makeTermOfficial(@ModelAttribute("KualiForm") AcademicCalendarForm academicCalendarForm, BindingResult result,
462                                          HttpServletRequest request, HttpServletResponse response) {
463         academicCalendarForm.setFieldsToSave(processDirtyFields(academicCalendarForm));
464 
465         int selectedLineIndex;
466 
467         AcademicTermWrapper termWrapper;
468 
469         String dialog=null;
470         if(academicCalendarForm.isOfficialCalendar()){
471             dialog = CalendarConstants.ACADEMIC_TERM_OFFICIAL_CONFIRMATION_DIALOG;
472         }else{
473             dialog = CalendarConstants.ACADEMIC_TERM_AND_CALENDAR_OFFICIAL_CONFIRMATION_DIALOG;
474         }
475 
476         // Dialog confirmation to make terms official
477         if (!hasDialogBeenDisplayed(dialog, academicCalendarForm)) {
478             selectedLineIndex= KSControllerHelper.getSelectedCollectionLineIndex(academicCalendarForm);
479             termWrapper= academicCalendarForm.getTermWrapperList().get(selectedLineIndex);
480             academicCalendarForm.setSelectedCollectionPath(academicCalendarForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH));
481             academicCalendarForm.setSelectedLineIndex(academicCalendarForm.getActionParamaterValue(UifParameters.SELECTED_LINE_INDEX));
482             academicCalendarForm.setMakeOfficialName(termWrapper.getName());
483             academicCalendarForm.setMakeOfficialIsSubterm(termWrapper.isSubTerm());
484             if(termWrapper.getParentTermInfo()!=null){
485                 academicCalendarForm.setMakeOfficialParentTermName(termWrapper.getParentTermInfo().getName());
486                 academicCalendarForm.setOfficialParentTerm(false);
487                 for(AcademicTermWrapper term : academicCalendarForm.getTermWrapperList()){
488                     if(term.getTermInfo().getId().equals(termWrapper.getParentTermInfo().getId())){
489                         academicCalendarForm.setOfficialParentTerm(term.isOfficial());
490                     }
491                 }
492             }
493 
494             //redirect back to client to display lightbox
495             return showDialog(dialog, academicCalendarForm, request, response);
496         }else{
497             if(hasDialogBeenAnswered(dialog,academicCalendarForm)){
498                 boolean confirm = getBooleanDialogResponse(dialog, academicCalendarForm, request, response);
499                 academicCalendarForm.getDialogManager().resetDialogStatus(dialog);
500                 if(!confirm){
501                     return getUIFModelAndView(academicCalendarForm);
502                 }
503             } else {
504                 selectedLineIndex= KSControllerHelper.getSelectedCollectionLineIndex(academicCalendarForm);
505                 termWrapper= academicCalendarForm.getTermWrapperList().get(selectedLineIndex);
506                 academicCalendarForm.setSelectedCollectionPath(academicCalendarForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH));
507                 academicCalendarForm.setSelectedLineIndex(academicCalendarForm.getActionParamaterValue(UifParameters.SELECTED_LINE_INDEX));
508                 academicCalendarForm.setMakeOfficialName(termWrapper.getName());
509                 academicCalendarForm.setMakeOfficialIsSubterm(termWrapper.isSubTerm());
510                 if(termWrapper.getParentTermInfo()!=null){
511                     academicCalendarForm.setMakeOfficialParentTermName(termWrapper.getParentTermInfo().getName());
512                     academicCalendarForm.setOfficialParentTerm(false);
513                     for(AcademicTermWrapper term : academicCalendarForm.getTermWrapperList()){
514                         if(term.getTermInfo().getId().equals(termWrapper.getParentTermInfo().getId())){
515                             academicCalendarForm.setOfficialParentTerm(term.isOfficial());
516                         }
517                     }
518                 }
519 
520 
521                 //redirect back to client to display lightbox
522                 return showDialog(dialog, academicCalendarForm, request, response);
523             }
524         }
525 
526         academicCalendarForm.getActionParameters().put(UifParameters.SELLECTED_COLLECTION_PATH, academicCalendarForm.getSelectedCollectionPath());
527         if (academicCalendarForm.getSelectedLineIndex() != null && academicCalendarForm.getSelectedLineIndex().indexOf(",") > -1) {
528             academicCalendarForm.getActionParameters().put(UifParameters.SELECTED_LINE_INDEX, academicCalendarForm.getSelectedLineIndex().substring(0,academicCalendarForm.getSelectedLineIndex().indexOf(",")));
529         } else {
530             academicCalendarForm.getActionParameters().put(UifParameters.SELECTED_LINE_INDEX, academicCalendarForm.getSelectedLineIndex());
531         }
532         selectedLineIndex= KSControllerHelper.getSelectedCollectionLineIndex(academicCalendarForm);
533         termWrapper= academicCalendarForm.getTermWrapperList().get(selectedLineIndex);
534 
535         boolean acalOfficial=true;
536 
537         if(!academicCalendarForm.isOfficialCalendar()){
538             acalOfficial = makeAcalOfficial(academicCalendarForm);
539         }
540 
541         if(acalOfficial){
542             makeTermOfficial(termWrapper,academicCalendarForm);
543         }
544         getAcalViewHelperService(academicCalendarForm).populateAcademicCalendar(academicCalendarForm.getAcademicCalendarInfo().getId(), academicCalendarForm);
545         academicCalendarForm.setDefaultTabToShow(CalendarConstants.ACAL_TERM_TAB);
546         return getUIFModelAndView(academicCalendarForm);
547     }
548 
549 
550     /**
551      *
552      * Improvement jira KSENROLL-3568  to clear all the fields at the client side instead of at server side.
553      * @param academicCalendarForm
554      * @param result
555      * @param request
556      * @param response
557      * @return
558      */
559     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=cancelAddingHoliday")
560     public ModelAndView cancelAddingHoliday(@ModelAttribute("KualiForm") AcademicCalendarForm academicCalendarForm, BindingResult result,
561                                             HttpServletRequest request, HttpServletResponse response) {
562         academicCalendarForm.setFieldsToSave(processDirtyFields(academicCalendarForm));
563         ((HolidayCalendarWrapper)academicCalendarForm.getNewCollectionLines().get("holidayCalendarList")).setId(StringUtils.EMPTY);
564 
565         return getUIFModelAndView(academicCalendarForm);
566     }
567 
568     private void setDeleteTermMessageWithContext(AcademicCalendarForm academicCalendarForm){
569         academicCalendarForm.setSelectedCollectionPath(academicCalendarForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH));
570         academicCalendarForm.setSelectedLineIndex(academicCalendarForm.getActionParamaterValue(UifParameters.SELECTED_LINE_INDEX));
571         academicCalendarForm.getActionParameters().put(UifParameters.SELLECTED_COLLECTION_PATH, academicCalendarForm.getSelectedCollectionPath());
572         if (academicCalendarForm.getSelectedLineIndex() != null && academicCalendarForm.getSelectedLineIndex().indexOf(",") > -1) {
573             academicCalendarForm.getActionParameters().put(UifParameters.SELECTED_LINE_INDEX, academicCalendarForm.getSelectedLineIndex().substring(0,academicCalendarForm.getSelectedLineIndex().indexOf(",")));
574         } else {
575             academicCalendarForm.getActionParameters().put(UifParameters.SELECTED_LINE_INDEX, academicCalendarForm.getSelectedLineIndex());
576         }
577 
578         int selectedLineIndex = KSControllerHelper.getSelectedCollectionLineIndex(academicCalendarForm);
579 
580         AcademicTermWrapper selectedTermWrapper = academicCalendarForm.getTermWrapperList().get(selectedLineIndex);
581         if(selectedTermWrapper.isSubTerm()){
582             academicCalendarForm.setMessageForDeleteTermOrSubterm(CalendarConstants.MESSAGE_CONFIRM_TO_DELETE_SUBTERM);
583         }else if(selectedTermWrapper.isHasSubterm()){
584             academicCalendarForm.setMessageForDeleteTermOrSubterm(CalendarConstants.MESSAGE_CONFIRM_TO_DELETE_TERM_WITH_SUBTERM);
585         }else{
586             academicCalendarForm.setMessageForDeleteTermOrSubterm(CalendarConstants.MESSAGE_CONFIRM_TO_DELETE_TERM_ONLY);
587         }
588     }
589 
590     /**
591      * This would mark a term for deletion if it's already there in the DB. If it's a newly added term, it just deletes
592      * that.
593      *
594      * @param academicCalendarForm
595      * @param result
596      * @param request
597      * @param response
598      * @return
599      */
600     @RequestMapping(params = "methodToCall=deleteTerm")
601     public ModelAndView deleteTerm(@ModelAttribute("KualiForm") AcademicCalendarForm academicCalendarForm, BindingResult result,
602                                    HttpServletRequest request, HttpServletResponse response) {
603         academicCalendarForm.setFieldsToSave(processDirtyFields(academicCalendarForm));
604 
605         String dialog = CalendarConstants.TERM_DELETE_CONFIRMATION_DIALOG;
606         if (!hasDialogBeenDisplayed(dialog, academicCalendarForm)) {
607             setDeleteTermMessageWithContext(academicCalendarForm);
608             //redirect back to client to display lightbox
609             return showDialog(dialog, academicCalendarForm, request, response);
610         }else{
611             if(hasDialogBeenAnswered(dialog,academicCalendarForm)){
612                 boolean confirmDelete = getBooleanDialogResponse(dialog, academicCalendarForm, request, response);
613                 academicCalendarForm.getDialogManager().resetDialogStatus(dialog);
614                 if(!confirmDelete){
615                     return getUIFModelAndView(academicCalendarForm);
616                 }
617             } else {
618                 setDeleteTermMessageWithContext(academicCalendarForm);
619                 //redirect back to client to display lightbox
620                 return showDialog(dialog, academicCalendarForm, request, response);
621             }
622         }
623 
624         academicCalendarForm.getActionParameters().put(UifParameters.SELLECTED_COLLECTION_PATH, academicCalendarForm.getSelectedCollectionPath());
625         if (academicCalendarForm.getSelectedLineIndex() != null && academicCalendarForm.getSelectedLineIndex().indexOf(",") > -1) {
626             academicCalendarForm.getActionParameters().put(UifParameters.SELECTED_LINE_INDEX, academicCalendarForm.getSelectedLineIndex().substring(0,academicCalendarForm.getSelectedLineIndex().indexOf(",")));
627         } else {
628             academicCalendarForm.getActionParameters().put(UifParameters.SELECTED_LINE_INDEX, academicCalendarForm.getSelectedLineIndex());
629         }
630 
631         int selectedLineIndex = KSControllerHelper.getSelectedCollectionLineIndex(academicCalendarForm);
632 
633         AcademicTermWrapper theTermWrapper = academicCalendarForm.getTermWrapperList().get(selectedLineIndex);
634 
635         if (!theTermWrapper.isNew()){
636             academicCalendarForm.getTermsToDeleteOnSave().add(theTermWrapper);
637         }
638         academicCalendarForm.getTermWrapperList().remove(selectedLineIndex);
639 
640         if(theTermWrapper.isHasSubterm())  { //get all subterms and remove them as well
641             List<AcademicTermWrapper> subTerms = theTermWrapper.getSubterms();
642             for(AcademicTermWrapper subTerm : subTerms){
643                 academicCalendarForm.getTermWrapperList().remove(subTerm);
644             }
645         }
646         return getUIFModelAndView(academicCalendarForm);
647     }
648 
649     /**
650      * Like term, this would mark a key date for deletion. If it's newly added, just deletes it.
651      *
652      * @param academicCalendarForm
653      * @param result
654      * @param request
655      * @param response
656      * @return
657      */
658     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=deleteKeyDate")
659     public ModelAndView deleteKeyDate(@ModelAttribute("KualiForm") AcademicCalendarForm academicCalendarForm, BindingResult result,
660                                       HttpServletRequest request, HttpServletResponse response) {
661         academicCalendarForm.setFieldsToSave(processDirtyFields(academicCalendarForm));
662         String selectedCollectionPath = academicCalendarForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH);
663         if (StringUtils.isBlank(selectedCollectionPath)) {
664             throw new RuntimeException("unable to determine the selected collection path");
665         }
666 
667         int selectedLineIndex = KSControllerHelper.getSelectedCollectionLineIndex(academicCalendarForm);
668 
669         String selectedTermIndex = StringUtils.substringBetween(selectedCollectionPath,"termWrapperList[","]");
670         String selectedKeyDateGroup = StringUtils.substringBetween(selectedCollectionPath,"keyDatesGroupWrappers[","]");
671 
672         AcademicTermWrapper termWrapper = academicCalendarForm.getTermWrapperList().get(Integer.parseInt(selectedTermIndex));
673         KeyDatesGroupWrapper keydateGroup = termWrapper.getKeyDatesGroupWrappers().get(Integer.parseInt(selectedKeyDateGroup));
674         KeyDateWrapper keyDateWrapper = keydateGroup.getKeydates().get(selectedLineIndex);
675 
676         if (StringUtils.isNotBlank(keyDateWrapper.getKeyDateInfo().getId())){
677             termWrapper.getKeyDatesToDeleteOnSave().add(keyDateWrapper);
678         }
679 
680         keydateGroup.getKeydates().remove(selectedLineIndex);
681 
682         return getUIFModelAndView(academicCalendarForm);
683 
684     }
685 
686     /**
687      * Like term, this would mark an exam period for deletion.
688      *
689      * @param academicCalendarForm
690      * @param result
691      * @param request
692      * @param response
693      * @return
694      */
695     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=markExamPeriodtoDelete")
696     public ModelAndView markExamPeriodtoDelete(@ModelAttribute("KualiForm") AcademicCalendarForm academicCalendarForm, BindingResult result,
697                                       HttpServletRequest request, HttpServletResponse response) {
698         academicCalendarForm.setFieldsToSave(processDirtyFields(academicCalendarForm));
699         String selectedCollectionPath = academicCalendarForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH);
700         if (StringUtils.isBlank(selectedCollectionPath)) {
701             throw new RuntimeException("unable to determine the selected collection path");
702         }
703 
704         int selectedLineIndex = KSControllerHelper.getSelectedCollectionLineIndex(academicCalendarForm);
705 
706         String selectedTermIndex = StringUtils.substringBetween(selectedCollectionPath,"termWrapperList[","]");
707         AcademicTermWrapper termWrapper = academicCalendarForm.getTermWrapperList().get(Integer.parseInt(selectedTermIndex));
708         ExamPeriodWrapper examPeriodWrapper = termWrapper.getExamdates().get(selectedLineIndex);
709 
710         if (StringUtils.isNotBlank(examPeriodWrapper.getExamPeriodInfo().getId())){
711             termWrapper.getExamPeriodsToDeleteOnSave().add(examPeriodWrapper);
712         }
713         termWrapper.getExamdates().remove(selectedLineIndex);
714 
715         return getUIFModelAndView(academicCalendarForm);
716 
717     }
718 
719     @RequestMapping(params = "methodToCall=deleteKeyDateGroup")
720     public ModelAndView deleteKeyDateGroup(@ModelAttribute("KualiForm") AcademicCalendarForm academicCalendarForm, BindingResult result,
721                                            HttpServletRequest request, HttpServletResponse response) {
722         academicCalendarForm.setFieldsToSave(processDirtyFields(academicCalendarForm));
723         String selectedCollectionPath = academicCalendarForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH);
724         if (StringUtils.isBlank(selectedCollectionPath)) {
725             throw new RuntimeException("unable to determine the selected collection path");
726         }
727 
728         int selectedLineIndex = KSControllerHelper.getSelectedCollectionLineIndex(academicCalendarForm);
729 
730         String selectedTermIndex = StringUtils.substringBetween(selectedCollectionPath, "termWrapperList[", "]");
731 
732         AcademicTermWrapper termWrapper = academicCalendarForm.getTermWrapperList().get(Integer.parseInt(selectedTermIndex));
733         KeyDatesGroupWrapper keydateGroup = termWrapper.getKeyDatesGroupWrappers().get(selectedLineIndex);
734         for (KeyDateWrapper keyDateWrapper : keydateGroup.getKeydates()) {
735             if (StringUtils.isNotBlank(keyDateWrapper.getKeyDateInfo().getId())){
736                 termWrapper.getKeyDatesToDeleteOnSave().add(keyDateWrapper);
737             }
738         }
739 
740         termWrapper.getKeyDatesGroupWrappers().remove(keydateGroup);
741 
742         return getUIFModelAndView(academicCalendarForm);
743 
744     }
745 
746     /**
747      * Removes the the selected holiday for delete from the list of holidays.
748      * No changes are made to database until save.
749      *
750      * @param academicCalendarForm
751      * @param result
752      * @param request
753      * @param response
754      * @return
755      */
756     @RequestMapping(params = "methodToCall=deleteHolidayCalendar")
757     public ModelAndView deleteHolidayCalendar(@ModelAttribute("KualiForm") AcademicCalendarForm academicCalendarForm, BindingResult result,
758                                               HttpServletRequest request, HttpServletResponse response) {
759         academicCalendarForm.setFieldsToSave(processDirtyFields(academicCalendarForm));
760         String selectedCollectionPath = academicCalendarForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH);
761         if (StringUtils.isBlank(selectedCollectionPath)) {
762             throw new RuntimeException("unable to determine the selected collection path");
763         }
764 
765         int selectedLineIndex = KSControllerHelper.getSelectedCollectionLineIndex(academicCalendarForm);
766 
767         academicCalendarForm.getHolidayCalendarList().remove(selectedLineIndex);
768 
769         return getUIFModelAndView(academicCalendarForm);
770 
771     }
772 
773     /**
774      * Removes the the selected event for delete from the list of events.
775      * Event is added to the list of events to be deleted during save if it is in the database.
776      * No changes are made to database until save.
777      *
778      * @param academicCalendarForm
779      * @param result
780      * @param request
781      * @param response
782      * @return
783      */
784     @RequestMapping(params = "methodToCall=deleteAcalEvent")
785     public ModelAndView deleteAcalEvent(@ModelAttribute("KualiForm") AcademicCalendarForm academicCalendarForm, BindingResult result,
786                                         HttpServletRequest request, HttpServletResponse response) {
787         academicCalendarForm.setFieldsToSave(processDirtyFields(academicCalendarForm));
788         String selectedCollectionPath = academicCalendarForm.getActionParamaterValue(UifParameters.SELLECTED_COLLECTION_PATH);
789         if (StringUtils.isBlank(selectedCollectionPath)) {
790             throw new RuntimeException("unable to determine the selected collection path");
791         }
792 
793         int selectedLineIndex = KSControllerHelper.getSelectedCollectionLineIndex(academicCalendarForm);
794         AcalEventWrapper deletedEvent = academicCalendarForm.getEvents().get(selectedLineIndex);
795         if(deletedEvent.getAcalEventInfo().getId()!=null){
796             academicCalendarForm.getEventsToDeleteOnSave().add(deletedEvent);
797         }
798 
799         academicCalendarForm.getEvents().remove(selectedLineIndex);
800 
801         return getUIFModelAndView(academicCalendarForm);
802 
803     }
804 
805     /**
806      * Method used to set Acal as official
807      */
808     @RequestMapping(params = "methodToCall=makeAcalOfficial")
809     public ModelAndView makeAcalOfficial(@ModelAttribute("KualiForm") AcademicCalendarForm acalForm, BindingResult result,
810                                          HttpServletRequest request, HttpServletResponse response) {
811         // Dialog confirmation to make the calendar official
812         String dialog = CalendarConstants.ACADEMIC_CALENDAR_OFFICIAL_CONFIRMATION_DIALOG;
813         if (!hasDialogBeenDisplayed(dialog, acalForm)) {
814             acalForm.setMakeOfficialName(acalForm.getAcademicCalendarInfo().getName());
815             //redirect back to client to display lightbox
816             return showDialog(dialog, acalForm, request, response);
817         }else{
818             if(hasDialogBeenAnswered(dialog,acalForm)){
819                 boolean confirm = getBooleanDialogResponse(dialog, acalForm, request, response);
820                 acalForm.getDialogManager().resetDialogStatus(dialog);
821                 if(!confirm){
822                     return getUIFModelAndView(acalForm);
823                 }
824             } else {
825 
826                 //redirect back to client to display lightbox
827                 return showDialog(dialog, acalForm, request, response);
828             }
829         }
830 
831         makeAcalOfficial(acalForm);
832 
833         return getUIFModelAndView(acalForm);
834     }
835 
836     public AcademicCalendarService getAcalService() {
837         if(acalService == null) {
838             acalService = (AcademicCalendarService) GlobalResourceLoader.getService(new QName(AcademicCalendarServiceConstants.NAMESPACE, AcademicCalendarServiceConstants.SERVICE_NAME_LOCAL_PART));
839         }
840         return this.acalService;
841     }
842 
843     public AcademicCalendarServiceFacade getAcademicCalendarServiceFacade() {
844         if(academicCalendarServiceFacade == null) {
845             academicCalendarServiceFacade = (AcademicCalendarServiceFacade) GlobalResourceLoader.getService(new QName(AcademicCalendarServiceConstants.FACADE_NAMESPACE, AcademicCalendarServiceConstants.FACADE_SERVICE_NAME_LOCAL_PART));
846         }
847         return this.academicCalendarServiceFacade;
848     }
849 
850     protected AcademicCalendarViewHelperService getAcalViewHelperService(AcademicCalendarForm acalForm){
851         AcademicCalendarViewHelperService viewHelperService = (AcademicCalendarViewHelperService) KSControllerHelper.getViewHelperService(acalForm);
852         return viewHelperService;
853     }
854 
855     /**
856      * Redirects from an academic calendar page to the calendar search page
857      *
858      * @param academicCalendarForm - Calendar form backing the page
859      * @param request - Http requests parameters
860      * @param urlParameters - Additional parameters to pass when redirecting
861      * @return The Calendar search page.
862      */
863     private ModelAndView redirectToSearch(AcademicCalendarForm academicCalendarForm,HttpServletRequest request, Properties urlParameters){
864         urlParameters.put("viewId", CalendarConstants.CALENDAR_SEARCH_VIEW);
865         urlParameters.put("methodToCall", KRADConstants.START_METHOD);
866         // UrlParams.SHOW_HISTORY and SHOW_HOME no longer exist
867         // https://fisheye.kuali.org/changelog/rice?cs=39034
868         // TODO KSENROLL-8469
869         //urlParameters.put(UifConstants.UrlParams.SHOW_HISTORY, BooleanUtils.toStringTrueFalse(false));
870         String uri = request.getRequestURL().toString().replace("academicCalendar","calendarSearch");
871         return performRedirect(academicCalendarForm, uri, urlParameters);
872     }
873 
874     /**
875      * Changes the state of the term from draft to official
876      *
877      * @param term - The Term to make official
878      * @param acalForm - Tee page form
879      * @return true if the term was made official successfully, false otherwise.
880      */
881     private boolean makeTermOfficial(AcademicTermWrapper term, AcademicCalendarForm acalForm){
882         AcademicCalendarViewHelperService viewHelperService = getAcalViewHelperService(acalForm);
883         StatusInfo statusInfo;
884         try {
885             // no need to check if the term is a sub term.  makeTermOfficialCascaded method works for both sub term and non-sub term
886             statusInfo = getAcademicCalendarServiceFacade().makeTermOfficialCascaded(term.getTermInfo().getId(), viewHelperService.createContextInfo());
887 
888             if (!statusInfo.getIsSuccess()) {
889                 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, RiceKeyConstants.ERROR_CUSTOM, statusInfo.getMessage());
890                 return false;
891             }
892             term.setTermInfo(getAcalService().getTerm(term.getTermInfo().getId(),viewHelperService.createContextInfo()));
893             if(term.isSubTerm()){ // update the parent term so the view displays the proper states (draft vs official)
894                 for(AcademicTermWrapper termWrapper : acalForm.getTermWrapperList()){
895                     if(termWrapper.getTermInfo().getId().equals(term.getParentTermInfo().getId())){
896                         TermInfo updatedParentTerm = getAcalService().getTerm(term.getParentTermInfo().getId(),viewHelperService.createContextInfo());
897                         // Make sure the parent term is updated in both the term and the termWrapperList.
898                         term.setParentTermInfo(updatedParentTerm);
899                         termWrapper.setTermInfo(updatedParentTerm); // the screen looks at this variable
900                     }
901                 }
902             }
903             for (KeyDatesGroupWrapper groupWrapper : term.getKeyDatesGroupWrappers()){
904                 for (KeyDateWrapper keyDateWrapper : groupWrapper.getKeydates()) {
905                     //...skip [unsaved] KeyDates that have null Id ...to avoid exception
906                     //note: the UI policy for this page: user must select 'Save' to save changes as make official does not save anything)
907                     if (keyDateWrapper.getKeyDateInfo() != null && keyDateWrapper.getKeyDateInfo().getId()!=null) {
908                         keyDateWrapper.setKeyDateInfo(getAcalService().getKeyDate(keyDateWrapper.getKeyDateInfo().getId(),viewHelperService.createContextInfo()));
909                     }
910                 }
911             }
912         } catch (Exception e) {
913             LOG.error("Make Official Failed for Term",e);
914             GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES,CalendarConstants.MessageKeys.ERROR_ACAL_SAVE_TERM_OFFICIAL_FAILED,e.getMessage());
915             return false;
916         }
917         return true;
918     }
919 
920     /**
921      * Attempts to make the academic calendar official.
922      *
923      * @param acalForm - Tee page form
924      * @return True if calendar state is successfully changed
925      */
926     private boolean makeAcalOfficial(AcademicCalendarForm acalForm){
927         AcademicCalendarViewHelperService viewHelperService = getAcalViewHelperService(acalForm);
928         try {
929             StatusInfo statusInfo = null;
930             statusInfo = getAcalService().changeAcademicCalendarState(acalForm.getAcademicCalendarInfo().getId(), AcademicCalendarServiceConstants.ACADEMIC_CALENDAR_OFFICIAL_STATE_KEY,viewHelperService.createContextInfo());
931             if (!statusInfo.getIsSuccess()){
932                 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, RiceKeyConstants.ERROR_CUSTOM, statusInfo.getMessage());
933                 return false;
934             } else{
935                 // Refresh form information
936                 acalForm.setAcademicCalendarInfo(getAcalService().getAcademicCalendar(acalForm.getAcademicCalendarInfo().getId(), viewHelperService.createContextInfo()));
937                 acalForm.setOfficialCalendar(true);
938                 for (AcalEventWrapper eventWrapper : acalForm.getEvents()) {
939                     eventWrapper.setAcalEventInfo(getAcalService().getAcalEvent(eventWrapper.getAcalEventInfo().getId(),viewHelperService.createContextInfo()));
940                 }
941             }
942         } catch (Exception e) {
943             LOG.error("Make Official Failed for Acal",e);
944             GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_ACAL_OFFICIAL_FAILED ,e.getMessage());
945             return false;
946         }
947         return true;
948     }
949 
950     /**
951      * Saves changes made to an academic calendar.
952      *
953      * @param academicCalendarForm - Form containing the data from the page.
954      * @return The updated calendar after save.
955      */
956     private AcademicCalendarForm saveAcademicCalendarDirtyFields(AcademicCalendarForm academicCalendarForm){
957         AcademicCalendarViewHelperService viewHelperService = getAcalViewHelperService(academicCalendarForm);
958 
959         // Convert Raw UI data and prepare it for save.
960         viewHelperService.populateAcademicCalendarDefaults(academicCalendarForm);
961         // Validate Acal
962         viewHelperService.validateAcademicCalendar(academicCalendarForm);
963 
964         if (GlobalVariables.getMessageMap().getErrorCount() > 0){
965             // If there are errors in the validation return current calendar without saving
966             return academicCalendarForm;
967         }
968 
969         // Create list of dirty fields by seperating string from page (See enroll.js:saveAcalPreProcess())
970         List<String> dirtyFields = processDirtyFields(academicCalendarForm);
971 
972         // Save the base Academic calendar info and refresh it in the form
973         AcademicCalendarInfo newAcal = saveAcal(academicCalendarForm.getAcademicCalendarInfo(), academicCalendarForm, viewHelperService);
974         academicCalendarForm.setAcademicCalendarInfo(newAcal);
975 
976         // Save the dirty field information
977         academicCalendarForm = saveDirtyFieldChanges(academicCalendarForm, dirtyFields,viewHelperService);
978 
979         // Check for new Acal Events and save them
980         academicCalendarForm = saveAcalEvents(academicCalendarForm,viewHelperService);
981 
982         // Check for deleted Acal Events and delete them from database
983         deleteAcalEvents(academicCalendarForm,viewHelperService);
984 
985         // Check for new terms and save them
986         academicCalendarForm = saveTerms(academicCalendarForm,viewHelperService);
987 
988         // Check for deleted terms and delete them from database
989         deleteTerms(academicCalendarForm, viewHelperService);
990 
991         // Check keydates in terms
992         for(int i = 0; i < academicCalendarForm.getTermWrapperList().size();i++){
993             // Check for new key dates and save them
994             academicCalendarForm = saveKeyDates(academicCalendarForm,i,viewHelperService);
995 
996             // Check for deleted key dates and delete them from database
997             AcademicTermWrapper term = academicCalendarForm.getTermWrapperList().get(i);
998             deleteKeyDates(term, viewHelperService);
999 
1000             // update instructionalDays
1001             try{
1002                 term.setInstructionalDays(getAcalService().getInstructionalDaysForTerm(term.getTermInfo().getId(), viewHelperService.createContextInfo()));
1003             }catch (Exception ex){
1004                 // If the lookup fails message user
1005                 LOG.error("Unable to load instructional days",ex);
1006                 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_CALCULATING_INSTRUCTIONAL_DAYS, term.getStartDate().toString(), term.getEndDate().toString());
1007             }
1008 
1009             //process exam periods
1010             academicCalendarForm = processExamPeriods(academicCalendarForm,i,viewHelperService);
1011         }
1012 
1013         // Reset values
1014         academicCalendarForm.getEventsToDeleteOnSave().clear();
1015         academicCalendarForm.getFieldsToSave().clear();
1016         academicCalendarForm.setDirtyFields("");
1017         academicCalendarForm.getTermsToDeleteOnSave().clear();
1018         academicCalendarForm.setNewCalendar(false);
1019 
1020         // Successful save message
1021         if(!(GlobalVariables.getMessageMap().getErrorCount()>0)){
1022             GlobalVariables.getMessageMap().addGrowlMessage("", CalendarConstants.MessageKeys.INFO_ACADEMIC_CALENDAR_SAVED, academicCalendarForm.getAcademicCalendarInfo().getName());
1023         }
1024 
1025         academicCalendarForm.setMeta(academicCalendarForm.getAcademicCalendarInfo().getMeta());
1026 
1027         // Reseting added collection items since they were saved
1028         academicCalendarForm.setAddedCollectionItems(new ArrayList<Object>());
1029 
1030         //sort term wrappers by start date
1031         viewHelperService.sortTermWrappers(academicCalendarForm.getTermWrapperList());
1032 
1033         // Return new form
1034         return academicCalendarForm;
1035     }
1036 
1037     /**
1038      * Cycles through the list of dirty fields, determines what property they correspond to and updates them.
1039      *
1040      * @param form - View form containing the Calendar information
1041      * @param dirtyFields - List of properties that have unsaved information
1042      * @param helperService - View Helper service
1043      * @return The updated form
1044      */
1045     private AcademicCalendarForm saveDirtyFieldChanges(AcademicCalendarForm form, List<String>dirtyFields, AcademicCalendarViewHelperService helperService){
1046         List<String> updatedFields = new ArrayList<String>();
1047 
1048         // Cycle through the dirty field list and save the individual properties.
1049         for(String field : dirtyFields){
1050             if(field.isEmpty()) continue;
1051 
1052             // Remove sub properties from the one to save
1053             field = field.substring(0,field.lastIndexOf("."));
1054 
1055             // Check if the property has already been saved.
1056             boolean alreadyUpdated = false;
1057             for(int j=0;j<updatedFields.size();j++){
1058                 if(field.compareTo(updatedFields.get(j))==0){
1059                     alreadyUpdated=true;
1060                     break;
1061                 }
1062             }
1063             if(alreadyUpdated) continue;
1064 
1065             // Add field to list of saved fields
1066             updatedFields.add(field);
1067 
1068             // Search for the save process for the field
1069             if(field.contains("newCollectionLines")){
1070                 // Catch known dirty field that are not handled by save
1071                 continue;
1072             }else if(field.contains("academicCalendarInfo")){
1073                 // Academic calendar info is always saved
1074                 continue;
1075             } else if(field.contains("events")){
1076 
1077                 // Save an individual event and refresh it in the form
1078                 int index = processFieldIndex(field);
1079                 AcalEventWrapper event = form.getEvents().get(index);
1080                 AcalEventWrapper newEvent = saveAcalEvent(event,form, helperService);
1081                 form.getEvents().set(index, newEvent);
1082 
1083             } else if(field.contains("keydates")){
1084 
1085                 // Save an individual key date change and refresh it in the form
1086                 int termIndex = processFieldIndex(field.substring(0, field.indexOf(".")));
1087                 int keyDateGroupIndex = processFieldIndex(field.substring(field.indexOf("."), field.lastIndexOf(".")));
1088                 int keyDateIndex = processFieldIndex( field.substring(field.lastIndexOf(".")));
1089                 KeyDateWrapper  keyDateWrapper = form.getTermWrapperList().get(termIndex).getKeyDatesGroupWrappers().get(keyDateGroupIndex).getKeydates().get(keyDateIndex);
1090                 KeyDateWrapper newKeyDateWrapper = saveKeyDate(keyDateWrapper, form.getTermWrapperList().get(termIndex), helperService);
1091                 form.getTermWrapperList().get(termIndex).getKeyDatesGroupWrappers().get(keyDateGroupIndex).getKeydates().set(keyDateIndex,newKeyDateWrapper);
1092 
1093             } else if (field.contains("examdates")) {
1094                 //exempt exam period from dirty field update for now
1095                 continue;
1096             } else if(field.contains("termWrapperList")){
1097 
1098                 // Save and individual even and refresh it in the form
1099                 int index = processFieldIndex(field);
1100                 AcademicTermWrapper term = form.getTermWrapperList().get(index);
1101                 AcademicTermWrapper newTerm = saveTerm(term, form, helperService);
1102                 form.getTermWrapperList().set(index,newTerm);
1103 
1104             } else {
1105                 // Signal that there is an unknown field found.
1106                 LOG.warn("Unknown field encounter during save: "+field);
1107             }
1108         }
1109         return form;
1110     }
1111 
1112     /**
1113      * Save changes to a calendar or create it if it has not already been saved
1114      *
1115      * @param acal - Calendar to be saved
1116      * @param form - View form containing the Calendar information
1117      * @param helperService - View Helper service
1118      * @return The updated calendar with information filled in from the save/create
1119      */
1120     private AcademicCalendarInfo saveAcal(AcademicCalendarInfo acal, AcademicCalendarForm form, AcademicCalendarViewHelperService helperService){
1121         // Process holiday calendar info and get calendar info base
1122         AcademicCalendarInfo acalInfo = processHolidayCalendars(form);
1123 
1124         // Save term to database
1125         try {
1126             if (StringUtils.isBlank(acal.getId())) {
1127                 // Fill in calendar information starting values
1128                 acalInfo.setStateKey(AcademicCalendarServiceConstants.ACADEMIC_CALENDAR_DRAFT_STATE_KEY);
1129                 acalInfo.setTypeKey(AcademicCalendarServiceConstants.ACADEMIC_CALENDAR_TYPE_KEY);
1130                 RichTextInfo rti = new RichTextInfo();
1131                 rti.setPlain(acalInfo.getName());
1132                 acalInfo.setDescr(rti);
1133 
1134                 // Save the key date to the database and return created information
1135                 AcademicCalendarInfo newAcal = getAcalService().createAcademicCalendar(AcademicCalendarServiceConstants.ACADEMIC_CALENDAR_TYPE_KEY, acalInfo, helperService.createContextInfo());
1136                 return newAcal;
1137             } else {
1138 
1139                 // Update the key date in the datebase and return updated information.
1140                 AcademicCalendarInfo updatedAcal = getAcalService().updateAcademicCalendar(acal.getId(), acal, helperService.createContextInfo());
1141                 return updatedAcal;
1142 
1143             }
1144         } catch (VersionMismatchException e){
1145 
1146             // If the calendar being worked on is out of date set up reload and message user
1147             form.setReload(true);
1148             GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, RiceKeyConstants.ERROR_CUSTOM,"You are saving an older version of this calendar. Please click on the reload button to get the newer version.");
1149             return acal;
1150 
1151         } catch(Exception e) {
1152 
1153             // If the save fails message user
1154             LOG.error("Add/update Academic calendar failed", e);
1155             GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_ACAL_SAVE_FAILED);
1156             return acal;
1157         }
1158     }
1159 
1160     /**
1161      * Cycles through the list of terms and saves any new terms detected.
1162      *
1163      * @param form - View form containing the Calendar information
1164      * @param helperService - View Helper service
1165      * @return The updated form.
1166      */
1167     private AcademicCalendarForm saveTerms(AcademicCalendarForm form, AcademicCalendarViewHelperService helperService){
1168         // Check for new terms and save them
1169         List<AcademicTermWrapper> subTermsToSave = new ArrayList<AcademicTermWrapper>();
1170         for(int i=0;i<form.getTermWrapperList().size();i++){
1171             AcademicTermWrapper term = form.getTermWrapperList().get(i);
1172             if(term.getTermInfo().getId()==null){
1173                 if(term.isSubTerm()){
1174                     // Subterms require the parent term to exist so wait and save all other terms are saved
1175                     subTermsToSave.add(term);
1176                 } else{
1177                     // Save the new term and refresh it in the form
1178                     AcademicTermWrapper newTerm = saveTerm(term,form,helperService);
1179                     form.getTermWrapperList().set(i,newTerm);
1180                 }
1181             }
1182         }
1183         // Save subterms from before
1184         for(AcademicTermWrapper subterm : subTermsToSave){
1185             int index = form.getTermWrapperList().indexOf(subterm);
1186             // Save the new term and refresh it in the form
1187             AcademicTermWrapper newTerm = saveTerm(subterm,form,helperService);
1188             form.getTermWrapperList().set(index,newTerm);
1189         }
1190 
1191         return form;
1192     }
1193 
1194     /**
1195      * Save changes to a term or create it if it has not already been saved
1196      *
1197      * @param termWrapper - The term to be saved
1198      * @param form - View form containing the Calendar information
1199      * @param helperService - View Helper service
1200      * @return The updated term with information filled in from the save/create
1201      */
1202     private AcademicTermWrapper saveTerm(AcademicTermWrapper termWrapper, AcademicCalendarForm form, AcademicCalendarViewHelperService helperService){
1203         // Create term info base
1204         TermInfo term = termWrapper.getTermInfo();
1205 
1206         // Fill in key date info from the wrapper
1207         term.setEndDate(termWrapper.getEndDate());
1208         term.setStartDate(termWrapper.getStartDate());
1209         term.setName(termWrapper.getName());
1210         term.setTypeKey(termWrapper.getTermType());
1211 
1212         // Save term to database
1213         try{
1214             if (termWrapper.isNew() && !termWrapper.isSubTerm()){
1215                 // If term is new and not a sub term.
1216                 // Save the term to the database and update wrapper information.
1217                 TermInfo newTerm = getAcalService().createTerm(termWrapper.getTermType(),term,helperService.createContextInfo());
1218                 termWrapper.setTermInfo(newTerm);
1219                 // Add term to the calendar
1220                 getAcalService().addTermToAcademicCalendar(form.getAcademicCalendarInfo().getId(),termWrapper.getTermInfo().getId(),helperService.createContextInfo());
1221             }else if(termWrapper.isNew() && termWrapper.isSubTerm()){
1222                 // If term is new and is a sub term.
1223 
1224                 //Find parent term
1225                 String parentTermTypeKey = termWrapper.getParentTerm();
1226                 TermInfo parentTermInfo = getParentTerm(form.getAcademicCalendarInfo().getId(), parentTermTypeKey,helperService);
1227 
1228                 // Check if Parent term exists
1229                 if(parentTermInfo == null){
1230                     // If not throw exception
1231                     throw new Exception("Parent Term does not exist. Therefor unable to save subterm.");
1232                 }else{
1233                     // If parent exist fill in parent information in term.
1234                     termWrapper.setParentTermInfo(parentTermInfo);
1235                     // Save the term to the database and update wrapper information.
1236                     TermInfo newTerm = getAcalService().createTerm(termWrapper.getTermType(),term,helperService.createContextInfo());
1237                     termWrapper.setTermInfo(newTerm);
1238                     // Add link to parent term
1239                     getAcalService().addTermToTerm(termWrapper.getParentTermInfo().getId(), termWrapper.getTermInfo().getId(), helperService.createContextInfo());
1240 
1241                     // Add link to calendar
1242                     getAcalService().addTermToAcademicCalendar(form.getAcademicCalendarInfo().getId(),termWrapper.getTermInfo().getId(),helperService.createContextInfo());
1243                 }
1244             }else {
1245                 //If term already exists
1246                 // Update the term in the datebase adn update wrapper information.
1247                 TermInfo updatedTerm = getAcalService().updateTerm(term.getId(),term,helperService.createContextInfo());
1248                 termWrapper.setTermInfo(updatedTerm);
1249             }
1250         }catch(Exception e){
1251             LOG.error("Save term has failed", e);
1252             GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_ACAL_SAVE_TERM_SAVE_FAILED,
1253                     termWrapper.getName(), e.getLocalizedMessage());
1254         }
1255 
1256         return termWrapper;
1257     }
1258 
1259     /**
1260      * Determines terms that have been deleted in the UI and deletes them from the database
1261      *
1262      * @param form - View form containing the Calendar information
1263      * @param helperService - View Helper service
1264      */
1265     private void deleteTerms(AcademicCalendarForm form, AcademicCalendarViewHelperService helperService){
1266         for (AcademicTermWrapper term : form.getTermsToDeleteOnSave()){
1267             try{
1268                 getAcademicCalendarServiceFacade().deleteTermCascaded(term.getTermInfo().getId(),helperService.createContextInfo());
1269             }catch(Exception e){
1270                 LOG.error("Delete term has failed",e);
1271                 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_DELETING,"Term",term.getName());
1272 
1273             }
1274         }
1275     }
1276 
1277     /**
1278      * Cycles through the key date groups and their list of key dates and saves any new keydates detected
1279      *
1280      * @param form - View form containing the Calendar information
1281      * @param termIndex - The index of the term.
1282      * @param helperService - View Helper service
1283      * @return The updated form.
1284      */
1285     private AcademicCalendarForm saveKeyDates(AcademicCalendarForm form, int termIndex, AcademicCalendarViewHelperService helperService){
1286         AcademicTermWrapper term = form.getTermWrapperList().get(termIndex);
1287         for( int j = 0; j<term.getKeyDatesGroupWrappers().size();j++){
1288             KeyDatesGroupWrapper keyDateGroup = term.getKeyDatesGroupWrappers().get(j);
1289             for(int k = 0; k<keyDateGroup.getKeydates().size();k++){
1290                 KeyDateWrapper keyDate = keyDateGroup.getKeydates().get(k);
1291                 if(keyDate.getKeyDateInfo().getId() ==  null){
1292                     KeyDateWrapper newKeyDate= saveKeyDate(keyDate,term,helperService);
1293                     form.getTermWrapperList().get(termIndex).getKeyDatesGroupWrappers().get(j).getKeydates().set(k,newKeyDate);
1294                 }
1295             }
1296         }
1297         return form;
1298     }
1299 
1300     /**
1301      * Save changes to a keydate or create it if it has not already been saved
1302      *
1303      * @param keyDateWrapper - KeyDate to be saved
1304      * @param term - Term containing the key date
1305      * @param helperService - View helper service
1306      * @return The updated keydate with information filled in from the save/create
1307      */
1308     private KeyDateWrapper saveKeyDate(KeyDateWrapper keyDateWrapper, AcademicTermWrapper term, AcademicCalendarViewHelperService helperService){
1309         // Create key date info base
1310         KeyDateInfo keyDate = keyDateWrapper.getKeyDateInfo();
1311 
1312         // Fill in key date info from the wrapper
1313         keyDate.setTypeKey(keyDateWrapper.getKeyDateType());
1314         keyDate.setName(keyDateWrapper.getKeyDateNameUI());
1315         keyDate.setIsAllDay(keyDateWrapper.isAllDay());
1316         keyDate.setIsDateRange(keyDateWrapper.isDateRange());
1317         keyDate.setStartDate(getDateInfoForKeyDate(keyDateWrapper.isAllDay(),keyDateWrapper.getStartDate(),keyDateWrapper.getStartTime(),keyDateWrapper.getStartTimeAmPm()));
1318         if (keyDateWrapper.isDateRange()){
1319             keyDate.setEndDate(getDateInfoForKeyDate(keyDateWrapper.isAllDay(),keyDateWrapper.getEndDate(),keyDateWrapper.getEndTime(),keyDateWrapper.getEndTimeAmPm()));
1320         } else{
1321             keyDate.setEndDate(null);
1322         }
1323 
1324         // Save Key date to database
1325         try{
1326             if (keyDateWrapper.isNew()){
1327                 // Save the key date to the database and update wrapper information.
1328                 keyDate.setStateKey(AtpServiceConstants.MILESTONE_DRAFT_STATE_KEY);
1329                 KeyDateInfo createdKeyDate = getAcalService().createKeyDate(term.getTermInfo().getId(),keyDate.getTypeKey(),keyDate,helperService.createContextInfo());
1330                 keyDateWrapper.setKeyDateInfo(createdKeyDate);
1331             } else {
1332                 // Update the key date in the datebase adn update wrapper information.
1333                 KeyDateInfo updatedKeyDate = getAcalService().updateKeyDate(keyDate.getId(), keyDate, helperService.createContextInfo());
1334                 keyDateWrapper.setKeyDateInfo(updatedKeyDate);
1335             }
1336         }catch(Exception e){
1337             LOG.error("Save keydate has failed",e);
1338             GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_ACAL_SAVE_TERM_KEYDATE_FAILED,keyDateWrapper.getKeyDateNameUI(),term.getName());
1339 
1340         }
1341 
1342         return keyDateWrapper;
1343     }
1344 
1345     /**
1346      * Cycles through the exam periods and process each of them
1347      *
1348      * @param form - View form containing the Calendar information
1349      * @param termIndex - The index of the term.
1350      * @param helperService - View Helper service
1351      * @return The updated form.
1352      */
1353     private AcademicCalendarForm processExamPeriods(AcademicCalendarForm form, int termIndex, AcademicCalendarViewHelperService helperService){
1354         //process add/update of exam period
1355         AcademicTermWrapper term = form.getTermWrapperList().get(termIndex);
1356         for(int i=0; i<term.getExamdates().size(); i++ ) {
1357             ExamPeriodWrapper examPeriodWrapper = term.getExamdates().get(i);
1358 
1359             ExamPeriodWrapper newExamPeriodWrapper = saveExamPeriod(form, examPeriodWrapper, term, termIndex, helperService);
1360             form.getTermWrapperList().get(termIndex).getExamdates().set(i,newExamPeriodWrapper);
1361         }
1362 
1363         //process the deletion of exam period
1364         for (ExamPeriodWrapper examPeriodToDelete : term.getExamPeriodsToDeleteOnSave()) {
1365             try {
1366                 getAcalService().deleteExamPeriod(examPeriodToDelete.getExamPeriodInfo().getId(), helperService.createContextInfo());
1367             } catch (Exception e) {
1368                 LOG.error("Delete exam period has failed",e);
1369                 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_DELETING,term.getName(),examPeriodToDelete.getExamPeriodNameUI());
1370             }
1371         }
1372         term.getExamPeriodsToDeleteOnSave().clear();
1373 
1374         return form;
1375     }
1376 
1377     /**
1378      * Save changes to a exam period or create it if it has not already been saved
1379      *
1380      * @param examPeriodWrapper - ExamPeriod to be saved
1381      * @param term - Term containing the key date
1382      * @param helperService - View helper service
1383      * @return The updated keydate with information filled in from the save/create
1384      */
1385     private ExamPeriodWrapper saveExamPeriod(AcademicCalendarForm form, ExamPeriodWrapper examPeriodWrapper, AcademicTermWrapper term, int termIndex, AcademicCalendarViewHelperService helperService){
1386         // Create exam period info base
1387         ExamPeriodInfo examPeriodInfo = examPeriodWrapper.getExamPeriodInfo();
1388         String examPeriodName = examPeriodWrapper.getExamPeriodNameUI() + " " + term.getName();
1389         // Fill in key date info from the wrapper
1390         examPeriodInfo.setTypeKey(examPeriodWrapper.getExamPeriodType());
1391         examPeriodInfo.setName(examPeriodName);
1392         examPeriodInfo.setEndDate(examPeriodWrapper.getEndDate());
1393         examPeriodInfo.setStartDate(examPeriodWrapper.getStartDate());
1394         examPeriodInfo.setStateKey(term.getTermInfo().getStateKey()); //the state of the exam period is the same as the term state
1395         setExamPeriodAttr(examPeriodInfo, AcademicCalendarServiceConstants.EXAM_PERIOD_EXCLUDE_SATURDAY_ATTR, String.valueOf(examPeriodWrapper.isExcludeSaturday()));
1396         setExamPeriodAttr(examPeriodInfo, AcademicCalendarServiceConstants.EXAM_PERIOD_EXCLUDE_SUNDAY_ATTR, String.valueOf(examPeriodWrapper.isExcludeSunday()));
1397 
1398         RichTextInfo rti = new RichTextInfo();
1399         rti.setPlain(examPeriodName);
1400         rti.setFormatted(examPeriodName);
1401         examPeriodInfo.setDescr(rti);
1402 
1403         // Save Exam Period to database
1404         try{
1405             if (examPeriodWrapper.isNew()){
1406                 // Save the exam period to the database and update wrapper information.
1407                 List<String> termTypeKeyList = new ArrayList<String>();
1408                 termTypeKeyList.add(term.getTermType());
1409                 ExamPeriodInfo createdExamPeriodInfo = getAcademicCalendarServiceFacade().addExamPeriod(examPeriodInfo.getTypeKey(), termTypeKeyList, examPeriodInfo, helperService.createContextInfo());
1410                 getAcalService().addExamPeriodToTerm(term.getTermInfo().getId(), createdExamPeriodInfo.getId(), helperService.createContextInfo());
1411                 examPeriodWrapper.setExamPeriodInfo(createdExamPeriodInfo);
1412             } else {
1413                 // Update the exam period in the datebase and update wrapper information.
1414                 ExamPeriodInfo updatedExamPeriodInfo = getAcalService().updateExamPeriod(examPeriodInfo.getId(), examPeriodInfo, helperService.createContextInfo());
1415                 examPeriodWrapper.setExamPeriodInfo(updatedExamPeriodInfo);
1416             }
1417         } catch (OperationFailedException oe){
1418             LOG.error("Save exam period has failed",oe);
1419             GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_ACAL_SAVE_TERM_EXAMPERIOD_FAILED,examPeriodWrapper.getExamPeriodNameUI(),term.getName() +". FEP is not allowed for the selected term.");
1420         }
1421         catch(Exception e){
1422             LOG.error("Save exam period has failed",e);
1423             GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_ACAL_SAVE_TERM_EXAMPERIOD_FAILED,examPeriodWrapper.getExamPeriodNameUI(),term.getName());
1424         }
1425 
1426         return examPeriodWrapper;
1427     }
1428 
1429 
1430     /**
1431      * Determines kay dates that have been deleted in the UI and deletes them from the database
1432      *
1433      * @param term - term wrapper from form
1434      * @param helperService - View Helper service
1435      */
1436     private void deleteKeyDates(AcademicTermWrapper term, AcademicCalendarViewHelperService helperService){
1437         for(KeyDateWrapper keyDate : term.getKeyDatesToDeleteOnSave()){
1438             try{
1439                 getAcalService().deleteKeyDate(keyDate.getKeyDateInfo().getId(),helperService.createContextInfo());
1440             }catch(Exception e){
1441                 LOG.error("Delete key date has failed",e);
1442                 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_DELETING,term.getName(),keyDate.getKeyDateNameUI());
1443             }
1444         }
1445         term.getKeyDatesToDeleteOnSave().clear();
1446     }
1447 
1448     /**
1449      * Cycles through the list of events and saves any new events detected
1450      *
1451      * @param form - View form containing the Calendar information
1452      * @param helperService - View Helper service
1453      * @return The updated form.
1454      */
1455     private AcademicCalendarForm saveAcalEvents(AcademicCalendarForm form, AcademicCalendarViewHelperService helperService){
1456         for(int i=0;i<form.getEvents().size();i++){
1457             AcalEventWrapper event = form.getEvents().get(i);
1458             if(event.getAcalEventInfo().getId()==null){
1459                 AcalEventWrapper newEvent = saveAcalEvent(event,form,helperService);
1460                 form.getEvents().set(i,newEvent);
1461             }
1462         }
1463         return form;
1464     }
1465 
1466     /**
1467      * Save changes to an event or create it if it has not already been saved
1468      *
1469      * @param event - The event to be created
1470      * @param form - View form containing the Calendar information
1471      * @param helperService - The view helper service
1472      * @return The updated event with the saved information from its creation.
1473      */
1474     private AcalEventWrapper saveAcalEvent(AcalEventWrapper event, AcademicCalendarForm form, AcademicCalendarViewHelperService helperService){
1475         // Create event info base
1476         AcalEventInfo eventInfo = event.getAcalEventInfo();
1477 
1478         // Fill in event info from the wrapper
1479         RichTextInfo rti = new RichTextInfo();
1480         rti.setPlain(event.getEventTypeKey());
1481         eventInfo.setDescr(rti);
1482         eventInfo.setTypeKey(event.getEventTypeKey());
1483         eventInfo.setIsAllDay(event.isAllDay());
1484         eventInfo.setIsDateRange(event.isDateRange());
1485         eventInfo.setStartDate(getDateInfoForKeyDate(event.isAllDay(),event.getStartDate(),event.getStartTime(),event.getStartTimeAmPm()));
1486         if(event.isDateRange()){
1487             eventInfo.setEndDate(getDateInfoForKeyDate(event.isAllDay(),event.getEndDate(),event.getEndTime(),event.getEndTimeAmPm()));
1488         } else{
1489             eventInfo.setEndDate(null);
1490         }
1491         // If calendar is official event is too
1492         if (!form.isOfficialCalendar()){
1493             eventInfo.setStateKey(AtpServiceConstants.MILESTONE_DRAFT_STATE_KEY);
1494         } else {
1495             eventInfo.setStateKey(AtpServiceConstants.MILESTONE_OFFICIAL_STATE_KEY);
1496         }
1497 
1498         // Save Event to database
1499         try{
1500             if(eventInfo.getId()==null){
1501                 // Save the event to the database and update wrapper information.
1502                 AcalEventInfo createdEventInfo = getAcalService().createAcalEvent(form.getAcademicCalendarInfo().getId(), eventInfo.getTypeKey(), eventInfo, helperService.createContextInfo());
1503                 event.setAcalEventInfo(createdEventInfo);
1504             }else{
1505                 // Update the event already in the database and update wrapper information.
1506                 AcalEventInfo updatedEventInfo = getAcalService().updateAcalEvent(eventInfo.getId(), eventInfo, helperService.createContextInfo());
1507                 event.setAcalEventInfo(updatedEventInfo);
1508             }
1509         }catch(Exception e){
1510             LOG.error("Save calendar event has failed" , e);
1511             GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_ACAL_SAVE_EVENT_FAILED,event.getEventTypeName());
1512 
1513         }
1514 
1515         return event;
1516     }
1517 
1518     /**
1519      * Determines events that have been deleted in the UI and deletes them from the database
1520      *
1521      * @param form - View form containing the Calendar information
1522      * @param helperService - View Helper service
1523      */
1524     private void deleteAcalEvents(AcademicCalendarForm form, AcademicCalendarViewHelperService helperService){
1525         for(AcalEventWrapper event : form.getEventsToDeleteOnSave()){
1526             try{
1527                 getAcalService().deleteAcalEvent(event.getAcalEventInfo().getId(),helperService.createContextInfo());
1528             }catch(Exception e){
1529                 LOG.error("Delete calendar event has failed" , e);
1530                 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, CalendarConstants.MessageKeys.ERROR_DELETING,"Calendar event",event.getEventTypeName());
1531             }
1532         }
1533     }
1534 
1535     /**
1536      * Finds and returns an array index of a property from its field name
1537      *
1538      * @param field - The property field string in the form propertyName[#]
1539      * @return The index of the array slot referenced in the property.
1540      */
1541     private int processFieldIndex(String field){
1542         String indexChar = field.substring(field.indexOf("[")+1, field.lastIndexOf("]"));
1543         return Integer.parseInt(indexChar);
1544     }
1545 
1546     /**
1547      * Finds and returns the parent term of a subterm
1548      *
1549      * @param acalId - Id of the calendar containing the subterm
1550      * @param parentTermTypeKey - The type key of the parent term
1551      * @param helperService - the view helper service
1552      * @return The term info for the parent term
1553      * @throws Exception - Exception thrown by the call to the academic calendar service
1554      */
1555     private TermInfo getParentTerm(String acalId, String parentTermTypeKey, AcademicCalendarViewHelperService helperService) throws Exception{
1556 
1557         List<TermInfo> termInfoList =  getAcalService().getTermsForAcademicCalendar(acalId, helperService.createContextInfo());
1558         for(TermInfo termInfo : termInfoList){
1559             if (parentTermTypeKey.equals(termInfo.getTypeKey())) {
1560                 return termInfo;
1561             }
1562         }
1563         return null;
1564     }
1565 
1566     /**
1567      * Creates a Date object with just date or date and time based on if the event is all day or not
1568      *
1569      * @param isAllDay - Whether event is all day or not
1570      * @param date - The MM/dd/yyyy date
1571      * @param time - A string of the time
1572      * @param ampm - A string of whether the time is am or pm
1573      * @return A completed date object based on isAllDay
1574      */
1575     private Date getDateInfoForKeyDate(boolean isAllDay, Date date, String time, String ampm){
1576         if(!isAllDay){
1577             return AcalCommonUtils.getDateWithTime(date, time, ampm);
1578         }
1579         return date;
1580     }
1581 
1582     /**
1583      * Compiles the list of Holiday ids saved in the form to a new list and adds it to the academicCalendarInfo.
1584      *
1585      * @param academicCalendarForm - View form containing the Calendar information
1586      * @return An updated academic calendar info with list of hcals
1587      */
1588     private AcademicCalendarInfo processHolidayCalendars(AcademicCalendarForm academicCalendarForm)    {
1589         AcademicCalendarInfo acalInfo = academicCalendarForm.getAcademicCalendarInfo();
1590         List<HolidayCalendarWrapper> holidayCalendarList = academicCalendarForm.getHolidayCalendarList();
1591         List<String> holidayCalendarIds = new ArrayList<String>();
1592         if (holidayCalendarList != null && !holidayCalendarList.isEmpty()) {
1593             for (HolidayCalendarWrapper hcWrapper : holidayCalendarList){
1594                 holidayCalendarIds.add(hcWrapper.getHolidayCalendarInfo().getId());
1595             }
1596         }
1597 
1598         // if the list from the form is empty, then all holiday calendars have been removed (or none have been assigned)
1599         acalInfo.setHolidayCalendarIds(holidayCalendarIds);
1600         academicCalendarForm.setAcademicCalendarInfo(acalInfo);
1601 
1602         return acalInfo;
1603     }
1604 
1605     /**
1606      * Processes the the string of dirty fields on the form and stores
1607      * (Mainly to prevent loss during redirects during dialogs)
1608      *
1609      * @param academicCalendarForm - View form containing the Calendar information
1610      * @return List of diry fields passed from the screen.
1611      */
1612     private List<String> processDirtyFields(AcademicCalendarForm academicCalendarForm){
1613         String[] tempFields = academicCalendarForm.getDirtyFields().split(",");
1614         List<String> dirtyFields = academicCalendarForm.getFieldsToSave();
1615         StringBuilder completeDirtyFields = new StringBuilder("");
1616         for(String field : tempFields){
1617             boolean alreadySeen = false;
1618             for(String field2 : dirtyFields){
1619                 if(field2.compareTo(field)==0){
1620                     alreadySeen=true;
1621                     break;
1622                 }
1623             }
1624             if(!alreadySeen){
1625                 dirtyFields.add(field);
1626 
1627             }
1628         }
1629         for(String field : dirtyFields){
1630             completeDirtyFields.append(field);
1631             completeDirtyFields.append(",");
1632         }
1633         academicCalendarForm.setDirtyFields(completeDirtyFields.toString());
1634         return dirtyFields;
1635 
1636     }
1637 
1638     /**
1639      * Override of the Krad lightbox return function to allow for returning to the controller without a redirect.
1640      * Redirect causes a page refresh.
1641      */
1642     @Override
1643     @RequestMapping(params = "methodToCall=returnFromLightbox")
1644     public ModelAndView returnFromLightbox(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
1645                                            HttpServletRequest request, HttpServletResponse response) {
1646 
1647         String newMethodToCall;
1648 
1649         // Save user responses from dialog
1650         DialogManager dm = form.getDialogManager();
1651         String dialogId = dm.getCurrentDialogId();
1652         if (dialogId == null) {
1653             newMethodToCall = "start";
1654         } else {
1655             dm.setDialogAnswer(dialogId, form.getDialogResponse());
1656             dm.setDialogExplanation(dialogId, form.getDialogExplanation());
1657             newMethodToCall = dm.getDialogReturnMethod(dialogId);
1658             dm.setCurrentDialogId(null);
1659         }
1660 
1661         // KSENROLL Code Start
1662         form.setMethodToCall(newMethodToCall);
1663 
1664         // Attempt to return to the controller method directly using reflection (look for the matching methodToCall)
1665         for (Method m : this.getClass().getMethods()) {
1666             RequestMapping a = m.getAnnotation(RequestMapping.class);
1667             if (a != null) {
1668                 String[] annotationsParams = a.params();
1669                 for (String param : annotationsParams) {
1670                     if (param.contains("methodToCall=" + newMethodToCall)) {
1671                         try {
1672                             return (ModelAndView) m.invoke(this, form, result, request, response);
1673                         } catch (IllegalAccessException iae) {
1674                             LOG.error("Reflection Invocation failed", iae);
1675                             throw new RuntimeException("Error using reflection in returnFromLightbox", iae);
1676                         } catch (InvocationTargetException ite) {
1677                             LOG.error("Reflection Invocation failed", ite);
1678                             throw new RuntimeException("Error using reflection in returnFromLightbox", ite);
1679                         } catch (IllegalArgumentException iae) {
1680                             LOG.error("Reflection Invocation failed", iae);
1681                             throw new RuntimeException("Error using reflection in returnFromLightbox", iae);
1682                         }
1683                     }
1684                 }
1685 
1686             }
1687         }
1688         // KSENROLL Code End
1689 
1690         // call intended controller method
1691         Properties props = new Properties();
1692         props.put(UifParameters.METHOD_TO_CALL, newMethodToCall);
1693         props.put(UifParameters.VIEW_ID, form.getViewId());
1694         props.put(UifParameters.FORM_KEY, form.getFormKey());
1695         props.put(UifParameters.AJAX_REQUEST, "false");
1696 
1697         return performRedirect(form, form.getFormPostUrl(), props);
1698     }
1699 
1700     /**
1701      * Override to process and save dirty fields when adding values.
1702      */
1703     @Override
1704     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=addLine")
1705     public ModelAndView addLine(@ModelAttribute("KualiForm") UifFormBase uifForm, BindingResult result,
1706                                 HttpServletRequest request, HttpServletResponse response) {
1707         ((AcademicCalendarForm)uifForm).setFieldsToSave(processDirtyFields((AcademicCalendarForm)uifForm));
1708         return super.addLine(uifForm,result,request,response);
1709     }
1710 
1711     private void setExamPeriodAttr(ExamPeriodInfo examPeriodInfo, String attrKey, String attrValue) {
1712         AttributeInfo attributeInfo = getExamPeriodAttrForKey(examPeriodInfo, attrKey);
1713         if (attributeInfo != null) {
1714             attributeInfo.setValue(attrValue);
1715         } else {
1716             attributeInfo = AcalCommonUtils.createAttribute(attrKey, attrValue);
1717             examPeriodInfo.getAttributes().add(attributeInfo);
1718         }
1719     }
1720 
1721     private AttributeInfo getExamPeriodAttrForKey(ExamPeriodInfo examPeriodInfo, String key) {
1722         for (AttributeInfo info : examPeriodInfo.getAttributes()) {
1723             if (info.getKey().equals(key)) {
1724                 return info;
1725             }
1726         }
1727         return null;
1728     }
1729 
1730 
1731 
1732 }