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