org.kuali.student.myplan.plan.controller.PlanController.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.student.myplan.plan.controller.PlanController.java

Source

/*
 * Copyright 2011 The Kuali Foundation
 * 
 * Licensed under the Educational Community License, Version 1.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.opensource.org/licenses/ecl1.php
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.kuali.student.myplan.plan.controller;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.map.ObjectMapper;
import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.krad.datadictionary.exception.DuplicateEntryException;
import org.kuali.rice.krad.uif.field.AttributeQueryResult;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.web.controller.UifControllerBase;
import org.kuali.rice.krad.web.form.UifFormBase;
import org.kuali.student.ap.framework.config.KsapFrameworkServiceLocator;
import org.kuali.student.ap.framework.context.CourseHelper;
import org.kuali.student.enrollment.academicrecord.service.AcademicRecordService;
import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingDisplayInfo;
import org.kuali.student.enrollment.courseoffering.dto.CourseOfferingInfo;
import org.kuali.student.enrollment.courseoffering.service.CourseOfferingService;
import org.kuali.student.myplan.academicplan.dto.LearningPlanInfo;
import org.kuali.student.myplan.academicplan.dto.PlanItemInfo;
import org.kuali.student.myplan.academicplan.infc.LearningPlan;
import org.kuali.student.myplan.academicplan.infc.PlanItem;
import org.kuali.student.myplan.academicplan.service.AcademicPlanService;
import org.kuali.student.myplan.academicplan.service.AcademicPlanServiceConstants;
import org.kuali.student.myplan.audit.dto.AuditReportInfo;
import org.kuali.student.myplan.audit.service.DegreeAuditService;
import org.kuali.student.myplan.audit.service.DegreeAuditServiceConstants;
import org.kuali.student.myplan.comment.dataobject.MessageDataObject;
import org.kuali.student.myplan.comment.service.CommentQueryHelper;
import org.kuali.student.myplan.comment.util.CommentHelper;
import org.kuali.student.myplan.config.UwMyplanServiceLocator;
import org.kuali.student.myplan.course.dataobject.ActivityOfferingItem;
import org.kuali.student.myplan.course.dataobject.CourseSummaryDetails;
import org.kuali.student.myplan.course.service.CourseDetailsInquiryHelperImpl;
import org.kuali.student.myplan.course.util.CourseSearchConstants;
import org.kuali.student.myplan.plan.PlanConstants;
import org.kuali.student.myplan.plan.dataobject.DeconstructedCourseCode;
import org.kuali.student.myplan.plan.dataobject.RecommendedItemDataObject;
import org.kuali.student.myplan.plan.form.PlanForm;
import org.kuali.student.myplan.plan.service.PlannedTermsHelperBase;
import org.kuali.student.myplan.plan.util.*;
import org.kuali.student.myplan.utils.GlobalConstants;
import org.kuali.student.myplan.utils.KSAPRoleUtils;
import org.kuali.student.myplan.utils.UserSessionHelper;
import org.kuali.student.r2.common.dto.AttributeInfo;
import org.kuali.student.r2.common.dto.ContextInfo;
import org.kuali.student.r2.common.dto.MetaInfo;
import org.kuali.student.r2.common.dto.RichTextInfo;
import org.kuali.student.r2.common.exceptions.*;
import org.kuali.student.r2.lum.course.dto.CourseInfo;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.HtmlUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;

import static org.springframework.util.StringUtils.hasText;

@Controller
@RequestMapping(value = "/plan/**")
public class PlanController extends UifControllerBase {

    private final Logger logger = Logger.getLogger(PlanController.class);

    private transient AcademicPlanService academicPlanService;

    private transient DegreeAuditService degreeAuditService;

    private transient CourseHelper courseHelper;

    private CommentHelper commentHelper;

    private PlanHelper planHelper;

    private UserSessionHelper userSessionHelper;

    private PlannedTermsHelperBase plannedTermsHelper;

    private transient CourseDetailsInquiryHelperImpl courseDetailsInquiryService;

    private transient CourseOfferingService courseOfferingService;

    //  Java to JSON outputter.
    private transient ObjectMapper mapper = new ObjectMapper();

    // Used for getting the term and year from Atp
    private transient AtpHelper atpHelper;

    private transient AcademicRecordService academicRecordService;

    @Override
    protected PlanForm createInitialForm(HttpServletRequest request) {
        return new PlanForm();
    }

    /**
     * plan Access form for student to provide viewing plan access to adviser
     *
     * @param form
     * @param result
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(params = "methodToCall=startPlanAccessForm")
    public ModelAndView startPlanAccessForm(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
            HttpServletRequest request, HttpServletResponse response) {
        super.start(form, result, request, response);

        /**
         * Initializing
         */
        PlanForm planForm = (PlanForm) form;
        List<LearningPlanInfo> plan = null;

        /**
         * Loading Bookmark Plan Items count and adviser sharing flag
         */
        try {

            plan = getAcademicPlanService().getLearningPlansForStudentByType(getUserSessionHelper().getStudentId(),
                    PlanConstants.LEARNING_PLAN_TYPE_PLAN, PlanConstants.CONTEXT_INFO);

            if (!CollectionUtils.isEmpty(plan)) {

                //A student should have only one learning plan associated to his Id
                LearningPlan learningPlan = plan.get(0);

                planForm.setEnableAdviserView(learningPlan.getShared().toString());

                List<PlanItemInfo> planItems = getAcademicPlanService().getPlanItemsInPlanByType(
                        learningPlan.getId(), PlanConstants.LEARNING_PLAN_ITEM_TYPE_WISHLIST,
                        PlanConstants.CONTEXT_INFO);

                if (!CollectionUtils.isEmpty(planItems)) {
                    planForm.setBookmarkedCount(planItems.size());
                }

            }

        } catch (Exception e) {

            return doOperationFailedError(planForm, "Could not load the plan items", e);

        }

        /**
         * Loading the messages count
         */
        List<MessageDataObject> messages = null;

        try {
            CommentQueryHelper commentQueryHelper = new CommentQueryHelper();
            messages = commentQueryHelper.getMessages(getUserSessionHelper().getStudentId());
        } catch (Exception e) {
            throw new RuntimeException("Could not retrieve messages.", e);
        }

        if (!CollectionUtils.isEmpty(messages)) {
            planForm.setMessagesCount(messages.size());
        }

        return getUIFModelAndView(planForm);
    }

    /**
     * Initial Plan page loading
     *
     * @param form
     * @param result
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(params = "methodToCall=start")
    public ModelAndView get(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
            HttpServletRequest request, HttpServletResponse response) {
        super.start(form, result, request, response);
        PlanForm planForm = (PlanForm) form;
        planForm.setNewUser(isNewUser());
        planForm.setStudent(KSAPRoleUtils.principalHasRole(getUserSessionHelper().getStudentId(),
                GlobalConstants.STUDENT_ROLE));
        LearningPlan learningPlan = getPlanHelper().getLearningPlan(getUserSessionHelper().getStudentId());
        if (learningPlan != null) {
            planForm.setLearningPlanId(learningPlan.getId());
        }
        return getUIFModelAndView(planForm);
    }

    /**
     * Used for initializing all the popups, dialogs, menus
     *
     * @param form
     * @param result
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(params = "methodToCall=startAddPlannedCourseForm")
    public ModelAndView start(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
            HttpServletRequest request, HttpServletResponse response) {
        /**
         * Initializing
         */
        super.start(form, result, request, response);
        // ignore the form returned by super.start()
        PlanForm planForm = (PlanForm) form;

        /**
         * Loading and returning quickAdd view if requested
         * Pre-populating the data for quickAdd view if requested for edit
         *
         */
        if (PlanConstants.ADD_DIALOG_PAGE.equals(form.getPageId())
                || PlanConstants.EDIT_NOTE_PAGE.equals(form.getPageId())
                || PlanConstants.ADD_RECOMMENDED_DIALOG_PAGE.equals(form.getPageId())) {

            if (hasText(planForm.getAtpId())) {
                String termYear = AtpHelper.atpIdToTermName(planForm.getAtpId());
                planForm.setTermName(termYear);
            } else {
                return doPageRefreshError(planForm, "Could not open Quick Add.", null);
            }

            if (hasText(planForm.getPlanItemId())) {
                try {
                    PlanItemInfo planItemInfo = getAcademicPlanService().getPlanItem(planForm.getPlanItemId(),
                            PlanConstants.CONTEXT_INFO);
                    if (PlanConstants.LEARNING_PLAN_ITEM_TYPE_BACKUP.equalsIgnoreCase(planItemInfo.getTypeKey())
                            && !planForm.isBackup()) {
                        return doPageRefreshError(planForm, "Plan item not found.", null);
                    }

                    if (PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED.equalsIgnoreCase(planItemInfo.getTypeKey())
                            && planForm.isBackup()) {
                        return doPageRefreshError(planForm, "Plan item not found.", null);
                    }
                    if (hasText(planItemInfo.getDescr().getPlain())) {
                        planForm.setNote(planItemInfo.getDescr().getPlain());
                    }
                    if (isPlaceHolderType(planItemInfo.getRefObjectType())) {
                        if (hasText(planItemInfo.getRefObjectId())) {
                            if (PlanConstants.PLACE_HOLDER_TYPE_COURSE_LEVEL
                                    .equals(planItemInfo.getRefObjectType())) {
                                planForm.setCourseCd(planItemInfo.getRefObjectId());
                            } else {
                                planForm.setType(PlanConstants.GENERAL_TYPE);
                                planForm.setGeneralPlaceholder(String.format("%s|%s", planItemInfo.getRefObjectId(),
                                        planItemInfo.getRefObjectType()));
                                planForm.setPlaceholderCode(EnumerationHelper.getEnumAbbrValForCodeByType(
                                        planItemInfo.getRefObjectId(), planItemInfo.getRefObjectType()));
                            }
                            if (planItemInfo.getCredit() != null) {
                                planForm.setCredit(String.valueOf(planItemInfo.getCredit().intValue()));
                            }
                        }
                    } else {
                        if (hasText(planItemInfo.getRefObjectId())) {
                            CourseInfo courseInfo = getCourseHelper().getCourseInfoByIdAndCd(
                                    planItemInfo.getRefObjectId(),
                                    getPlanHelper().getCrossListedCourse(planItemInfo.getAttributes()));
                            if (courseInfo != null && hasText(courseInfo.getCode())) {
                                planForm.setCourseCd(courseInfo.getCode());
                            }
                        }
                    }
                } catch (DoesNotExistException e) {
                    return doPageRefreshError(planForm,
                            "PlanItem with Id:" + planForm.getPlanItemId() + " does not exist", e);
                } catch (Exception e) {
                    return doOperationFailedError(planForm, "Could not open Quick Add.", null);
                }
            }
            return getUIFModelAndView(planForm);
        }

        //Plan activities are needed only for move, copy, delete functionality
        List<String> activitiesRequiredPages = Arrays.asList(PlanConstants.MOVE_DIALOG_PAGE,
                PlanConstants.COPY_DIALOG_PAGE, PlanConstants.DELETE_DIALOG_PAGE);
        PlanItemInfo planItem = null;
        String planItemAtpId = null;

        /**
         * Loading planItem for all Planned Items in plan with a planItemId (planned, backup, wishList)
         */
        if (planForm.getPlanItemId() != null) {

            try {

                planItem = getAcademicPlanService().getPlanItem(planForm.getPlanItemId(),
                        PlanConstants.CONTEXT_INFO);

                if (planItem != null) {
                    if (!CollectionUtils.isEmpty(planItem.getPlanPeriods())) {
                        //Assuming plan Item can only have one plan period
                        planItemAtpId = planItem.getPlanPeriods().get(0);
                        if (planForm.getAtpId() != null && !planForm.getAtpId().equals(planItemAtpId)) {
                            return doPageRefreshError(planForm, "Plan item quarter is changed.", null);
                        }
                        if (planItemAtpId.equalsIgnoreCase(planForm.getAtpId()) && !planForm.isSetToPlanning()) {
                            planForm.setSetToPlanning(AtpHelper.isAtpSetToPlanning(planItemAtpId));
                        }
                        if (planForm.getAtpId() == null) {
                            planForm.setAtpId(planItemAtpId);
                        }
                        planForm.setTermName(AtpHelper.atpIdToTermName(planForm.getAtpId()));
                    }

                    planForm.setAdviserName(
                            getUserSessionHelper().getCapitalizedName(planItem.getMeta().getCreateId()));
                    planForm.setDateAdded(planItem.getMeta().getCreateTime());

                    if (PlanConstants.LEARNING_PLAN_ITEM_TYPE_BACKUP.equalsIgnoreCase(planItem.getTypeKey())
                            && !planForm.isBackup()) {
                        return doPageRefreshError(planForm, "Plan item not found.", null);
                    }

                    if (PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED.equalsIgnoreCase(planItem.getTypeKey())
                            && planForm.isBackup()) {
                        return doPageRefreshError(planForm, "Plan item not found.", null);
                    }

                    if (PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED.equals(planItem.getTypeKey())) {
                        planForm.setOwner(
                                getUserSessionHelper().getCurrentUserId().equals(planItem.getMeta().getCreateId()));
                        planForm.setRecommended(true);
                    }

                    if (hasText(planItem.getDescr().getPlain()) && !planForm.isSetToPlanning()
                            && planForm.isRecommended()) {
                        planForm.setAdviserNote(planItem.getDescr().getPlain());
                    } else {
                        planForm.setNote(planItem.getDescr().getPlain());
                    }

                    if (PlanConstants.PLACE_HOLDER_TYPE_GEN_ED.equals(planItem.getRefObjectType())
                            || PlanConstants.PLACE_HOLDER_TYPE.equals(planItem.getRefObjectType())) {
                        planForm.setGeneralPlaceholder(
                                String.format("%s|%s", planItem.getRefObjectId(), planItem.getRefObjectType()));
                        if (planItem.getCredit() != null) {
                            planForm.setCredit(String.valueOf(planItem.getCredit().intValue()));
                        }
                        planForm.setPlaceholderCode(EnumerationHelper.getEnumAbbrValForCodeByType(
                                planItem.getRefObjectId(), planItem.getRefObjectType()));
                        planForm.setPlaceholderTitle(EnumerationHelper
                                .getEnumValueForCodeByType(planItem.getRefObjectId(), planItem.getRefObjectType()));
                        planForm.setType(PlanConstants.GENERAL_TYPE);
                        return getUIFModelAndView(planForm);
                    } else if (PlanConstants.PLACE_HOLDER_TYPE_COURSE_LEVEL.equals(planItem.getRefObjectType())) {
                        planForm.setCourseCd(planItem.getRefObjectId());
                        DeconstructedCourseCode courseCode = getCourseHelper()
                                .getCourseDivisionAndNumber(planItem.getRefObjectId());
                        Map<String, String> subjectAreas = OrgHelper.getTrimmedSubjectAreas();
                        String subjectTitle = subjectAreas.get(courseCode.getSubject());
                        String subjectLevel = courseCode.getNumber().toUpperCase().replace(
                                CourseSearchConstants.COURSE_LEVEL_XX, CourseSearchConstants.COURSE_LEVEL_ZERO);
                        planForm.setPlaceholderTitle(String.format("%s %s level", subjectTitle, subjectLevel));
                        if (planItem.getCredit() != null) {
                            planForm.setCredit(String.valueOf(planItem.getCredit().intValue()));
                        }
                        return getUIFModelAndView(planForm);
                    } else {
                        planForm.setCourseId(planItem.getRefObjectId());
                        planForm.setCode(getPlanHelper().getCrossListedCourse(planItem.getAttributes()));
                    }

                } else {

                    return doPageRefreshError(planForm, "Plan item not found.", null);

                }
            } catch (Exception e) {
                return doPageRefreshError(planForm, "Plan item not found.", e);
            }

        }

        //TODO: Clean up with courseId removal
        if (StringUtils.isEmpty(planForm.getCourseId())) {

            return doOperationFailedError(planForm, "Could not initialize form because Course ID was missing.",
                    null);

        }

        planForm.setTermName(AtpHelper.atpIdToTermName(planForm.getAtpId()));

        /**
         * Populated the course summary details using the courseId or the versionIndependentId (planItem refObjId)
         */
        try {

            planForm.setCourseSummaryDetails(getCourseDetailsInquiryService()
                    .retrieveCourseSummaryByIdAndCd(planForm.getCourseId(), planForm.getCode()));
            if (StringUtils.isEmpty(planForm.getCode()) && planForm.getCourseSummaryDetails() != null
                    && planForm.getCourseSummaryDetails().getCourseId() != null) {
                planForm.setCode(planForm.getCourseSummaryDetails().getCode());
            }
        } catch (Exception e) {

            planForm.setCourseSummaryDetails(new CourseSummaryDetails());
            GlobalVariables.getMessageMap().clearErrorMessages();
            GlobalVariables.getMessageMap().putErrorForSectionId(PlanConstants.ACTION_MENU_PAGE_ID,
                    PlanConstants.ERROR_KEY_UNKNOWN_COURSE);
            return doPageRefreshError(planForm, "Could not load course details.", e);

        }

        /**
         * populating the Plan related Information
         */
        planForm.setPlannedCourseSummary(getCourseDetailsInquiryService().getPlannedCourseSummaryByIdAndCd(
                planForm.getCourseId(), planForm.getCode(), getUserSessionHelper().getStudentId()));

        /*Setting the atpId to the first recommended unplanned term */
        if (!CollectionUtils.isEmpty(planForm.getPlannedCourseSummary().getRecommendedItemDataObjects())
                && !PlanConstants.RECOMMENDED_DIALOG_PAGE.equals(planForm.getPageId())) {
            for (RecommendedItemDataObject recommendedItemDataObject : planForm.getPlannedCourseSummary()
                    .getRecommendedItemDataObjects()) {
                if (!recommendedItemDataObject.isPlanned()
                        && AtpHelper.isAtpSetToPlanning(recommendedItemDataObject.getAtpId())) {
                    planForm.setAtpId(recommendedItemDataObject.getAtpId());
                    break;
                }
            }
        }

        /**
         * Populating the planActivities (Activity Offerings which are planned)
         */
        if (activitiesRequiredPages.contains(form.getPageId()) && planItemAtpId != null) {
            String atpId = CollectionUtils.isEmpty(planItem.getPlanPeriods()) ? null
                    : planItem.getPlanPeriods().get(0);
            if (planForm.getCourseSummaryDetails() != null && planForm.getCourseSummaryDetails().getCode() != null
                    && AtpHelper.isAtpSetToPlanning(atpId)) {
                planForm.setPlanActivities(
                        getPlannedActivitiesByCourseAndTerm(planForm.getCourseSummaryDetails().getCode(), atpId));
            }
        }

        /**
         *  For academic record Item (with No planItem exists) instead of atpId,
         *  acadRecAtpId is used to populate the setToPlanning Flag
         */
        if (planForm.getAcadRecAtpId() != null && !planForm.isSetToPlanning()) {

            planForm.setSetToPlanning(AtpHelper.isAtpSetToPlanning(planForm.getAcadRecAtpId()));

        }

        return getUIFModelAndView(planForm);
    }

    /**
     * Move a planned course from plan to backup
     *
     * @param form
     * @param result
     * @param httprequest
     * @param httpresponse
     * @return
     */
    @RequestMapping(params = "methodToCall=plannedToBackup")
    public ModelAndView plannedToBackup(@ModelAttribute("KualiForm") PlanForm form, BindingResult result,
            HttpServletRequest httprequest, HttpServletResponse httpresponse) {

        if (getUserSessionHelper().isAdviser()) {
            return doAdviserAccessError(form, "Adviser Access Denied", null);
        }

        String planItemId = form.getPlanItemId();
        if (StringUtils.isEmpty(planItemId)) {
            return doOperationFailedError(form, "Plan Item ID was missing.", null);
        }

        String planItemAtpId = null;

        //  Verify the type is planned, change to backup, update, make events (delete, add, update credits).
        PlanItemInfo planItem = null;
        try {
            planItem = getAcademicPlanService().getPlanItem(planItemId, PlanConstants.CONTEXT_INFO);
        } catch (DoesNotExistException e) {
            return doPageRefreshError(form, "PlanItem with Id:" + planItemId + " doesnot exist", e);
        } catch (Exception e) {
            return doOperationFailedError(form, "Could not fetch plan item.", e);
        }

        if (planItem != null && !CollectionUtils.isEmpty(planItem.getPlanPeriods())) {
            planItemAtpId = planItem.getPlanPeriods().get(0);
        } else {
            return doOperationFailedError(form, "Could not fetch plan Item", null);
        }

        //  Verify that the plan item type is "planned".
        if (!PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED.equals(planItem.getTypeKey())) {
            return doPageRefreshError(form, "Move planned item was not type planned.", null);
        }

        //  Validate: Capacity.
        boolean hasCapacity = false;
        try {
            hasCapacity = isAtpHasCapacity(getPlanHelper().getLearningPlan(getUserSessionHelper().getStudentId()),
                    planItemAtpId, PlanConstants.LEARNING_PLAN_ITEM_TYPE_BACKUP);
        } catch (RuntimeException e) {
            return doOperationFailedError(form, "Could not validate capacity for new plan item.", e);
        }

        if (!hasCapacity) {
            return doPlanCapacityExceededError(form, PlanConstants.LEARNING_PLAN_ITEM_TYPE_BACKUP);
        }

        //  Load course summary details.
        CourseSummaryDetails courseDetails = null;
        if (!isPlaceHolderType(planItem.getRefObjectType())) {
            try {
                courseDetails = getCourseDetailsInquiryService().retrieveCourseSummaryByIdAndCd(
                        planItem.getRefObjectId(), getPlanHelper().getCrossListedCourse(planItem.getAttributes()));
            } catch (Exception e) {
                return doOperationFailedError(form, "Unable to retrieve Course Details.", e);
            }
        } else {
            courseDetails = new CourseSummaryDetails();
        }

        //  Make removed event before updating the plan item.
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> removeEvent = makeRemoveEvent(planItem, courseDetails,
                form, null);

        //  Update
        planItem.setTypeKey(PlanConstants.LEARNING_PLAN_ITEM_TYPE_BACKUP);
        try {
            getAcademicPlanService().updatePlanItem(planItemId, planItem,
                    getUserSessionHelper().makeContextInfoInstance());
        } catch (Exception e) {
            return doOperationFailedError(form, "Could not update plan item.", e);
        }

        //  Make events (delete, add, update credits).
        //  Set the javascript event(s) that should be thrown in the UI.
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();

        events.putAll(removeEvent);
        events.putAll(makeAddUpdateEvent(planItem, courseDetails, form, false));

        if (courseDetails != null && hasText(courseDetails.getCode())) {
            addStatusAlertEvents(courseDetails.getCode(), planItemAtpId, events, false);
        }

        events.putAll(makeUpdateTotalCreditsEvent(planItemAtpId,
                PlanConstants.JS_EVENT_NAME.UPDATE_NEW_TERM_TOTAL_CREDITS));

        form.setJavascriptEvents(events);

        //  Pass the ATP name in the params.
        String[] params = { makeLinkToAtp(planItemAtpId, AtpHelper.atpIdToTermName(planItemAtpId)) };
        return doPlanActionSuccess(form, PlanConstants.SUCCESS_KEY_PLANNED_ITEM_MARKED_BACKUP, params);
    }

    /**
     * Move a planned course from backup to plan
     *
     * @param form
     * @param result
     * @param httprequest
     * @param httpresponse
     * @return
     */
    @RequestMapping(params = "methodToCall=backupToPlanned")
    public ModelAndView backupToPlanned(@ModelAttribute("KualiForm") PlanForm form, BindingResult result,
            HttpServletRequest httprequest, HttpServletResponse httpresponse) {

        if (getUserSessionHelper().isAdviser()) {
            return doAdviserAccessError(form, "Adviser Access Denied", null);
        }

        String planItemId = form.getPlanItemId();
        if (StringUtils.isEmpty(planItemId)) {
            return doOperationFailedError(form, "Plan Item ID was missing.", null);
        }

        String planItemAtpId = null;

        //  Verify type backup, change to planned, update, make events (delete, add, update credits).
        PlanItemInfo planItem = null;
        try {
            planItem = getAcademicPlanService().getPlanItem(planItemId, PlanConstants.CONTEXT_INFO);
        } catch (DoesNotExistException e) {
            return doPageRefreshError(form, "PlanItem with Id:" + planItemId + " doesnot exist", e);
        } catch (Exception e) {
            return doOperationFailedError(form, "Could not fetch plan item.", e);
        }

        if (planItem != null && !CollectionUtils.isEmpty(planItem.getPlanPeriods())) {
            planItemAtpId = planItem.getPlanPeriods().get(0);
        } else {
            return doOperationFailedError(form, "Could not fetch plan Item", null);
        }

        //  Verify that the plan item type is "backup".
        if (!PlanConstants.LEARNING_PLAN_ITEM_TYPE_BACKUP.equals(planItem.getTypeKey())) {
            return doPageRefreshError(form, "Move planned item was not type backup.", null);
        }

        //  Validate: Capacity.
        boolean hasCapacity = false;
        try {
            hasCapacity = isAtpHasCapacity(getPlanHelper().getLearningPlan(getUserSessionHelper().getStudentId()),
                    planItemAtpId, PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED);
        } catch (RuntimeException e) {
            return doOperationFailedError(form, "Could not validate capacity for new plan item.", e);
        }

        if (!hasCapacity) {
            return doPlanCapacityExceededError(form, PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED);
        }

        //  Load course summary details.
        CourseSummaryDetails courseDetails = null;
        if (!isPlaceHolderType(planItem.getRefObjectType())) {
            try {
                courseDetails = getCourseDetailsInquiryService().retrieveCourseSummaryByIdAndCd(
                        planItem.getRefObjectId(), getPlanHelper().getCrossListedCourse(planItem.getAttributes()));
            } catch (Exception e) {
                return doOperationFailedError(form, "Unable to retrieve Course Details.", e);
            }
        } else {
            courseDetails = new CourseSummaryDetails();
        }

        //  Make removed event before updating the plan item.
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> removeEvent = makeRemoveEvent(planItem, courseDetails,
                form, null);

        //  Set type to "planned".
        planItem.setTypeKey(PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED);

        //  Update
        try {
            getAcademicPlanService().updatePlanItem(planItemId, planItem,
                    getUserSessionHelper().makeContextInfoInstance());
        } catch (Exception e) {
            return doOperationFailedError(form, "Could not update plan item.", e);
        }

        //  Make events (delete, add, update credits).
        //  Set the javascript event(s) that should be thrown in the UI.
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();

        events.putAll(removeEvent);
        events.putAll(makeAddUpdateEvent(planItem, courseDetails, form, false));

        if (courseDetails != null && hasText(courseDetails.getCode())) {
            addStatusAlertEvents(courseDetails.getCode(), planItemAtpId, events, false);
        }

        events.putAll(makeUpdateTotalCreditsEvent(planItemAtpId,
                PlanConstants.JS_EVENT_NAME.UPDATE_NEW_TERM_TOTAL_CREDITS));

        form.setJavascriptEvents(events);

        String[] params = { makeLinkToAtp(planItemAtpId, AtpHelper.atpIdToTermName(planItemAtpId)) };
        return doPlanActionSuccess(form, PlanConstants.SUCCESS_KEY_PLANNED_ITEM_MARKED_PLANNED, params);
    }

    /**
     * Move Planned courses to requested terms
     *
     * @param form
     * @param result
     * @param httprequest
     * @param httpresponse
     * @return
     */
    @RequestMapping(params = "methodToCall=movePlannedCourse")
    public ModelAndView movePlannedCourse(@ModelAttribute("KualiForm") PlanForm form, BindingResult result,
            HttpServletRequest httprequest, HttpServletResponse httpresponse) {
        if (getUserSessionHelper().isAdviser()) {
            return doAdviserAccessError(form, "Adviser Access Denied", null);
        }
        /**
         * This method needs a Plan Item ID and an ATP ID.
         */
        String planItemId = form.getPlanItemId();

        if (StringUtils.isEmpty(planItemId)) {
            return doOperationFailedError(form, "Plan Item ID was missing.", null);
        }

        //  Further validation of ATP IDs will happen in the service validation methods.
        if (StringUtils.isEmpty(form.getAtpId())) {
            return doOperationFailedError(form, "Term Year value missing", null);
        }

        String newAtpId = form.getAtpId();
        //  Can't validate further up because the new ATP ID can be "other".
        if (!AtpHelper.isAtpIdFormatValid(newAtpId)) {
            return doOperationFailedError(form, String.format("ATP ID [%s] was not formatted properly.", newAtpId),
                    null);
        }

        PlanItemInfo planItem = null;
        try {
            // First load the plan item and retrieve the courseId
            planItem = getAcademicPlanService().getPlanItem(planItemId, PlanConstants.CONTEXT_INFO);
        } catch (DoesNotExistException e) {
            return doPageRefreshError(form, "PlanItem with Id:" + planItemId + " doesnot exist", e);
        } catch (Exception e) {
            return doOperationFailedError(form, "Could not fetch plan item.", e);
        }

        if (planItem == null) {
            return doOperationFailedError(form, String.format("Could not fetch plan item."), null);
        }

        //  Lookup course details as they will be needed for errors.
        CourseSummaryDetails courseDetails = null;
        if (!isPlaceHolderType(planItem.getRefObjectType())) {
            try {
                courseDetails = getCourseDetailsInquiryService().retrieveCourseSummaryByIdAndCd(
                        planItem.getRefObjectId(), getPlanHelper().getCrossListedCourse(planItem.getAttributes()));
            } catch (Exception e) {
                return doOperationFailedError(form, "Unable to retrieve Course Details.", null);
            }
        } else {
            courseDetails = new CourseSummaryDetails();
        }

        //  Make sure there isn't a plan item for the same course id in the destination ATP.
        // unless maybe it's a placeholder? could go either way, but assume user actually wants additional placeholder
        if (!isPlaceHolderType(planItem.getRefObjectType())) {
            PlanItemInfo existingPlanItem = null;
            try {
                existingPlanItem = getPlanHelper().getPlannedOrBackupPlanItem(planItem.getRefObjectId(),
                        getPlanHelper().getCrossListedCourse(planItem.getAttributes()), newAtpId);
            } catch (RuntimeException e) {
                return doOperationFailedError(form, "Query for existing plan item failed.", null);
            }

            if (existingPlanItem != null) {
                String[] params = { courseDetails.getCode(), AtpHelper.atpIdToTermName(newAtpId) };
                return doErrorPage(form, PlanConstants.ERROR_KEY_PLANNED_ITEM_ALREADY_EXISTS, params);
            }
        }

        /*Remove the Planned Sections for this course before moving the course to another term*/
        Map<String, String> planItemsToRemove = new HashMap<String, String>();

        if (courseDetails != null && courseDetails.getCourseId() != null && planItem != null
                && planItem.getPlanPeriods().size() > 0) {
            planItemsToRemove = getPlannedSectionsBySectionCd(courseDetails.getCode(), planItem, false, null);
        }

        try {

            if (!planItemsToRemove.isEmpty()) {
                for (String planItemIdToRemove : planItemsToRemove.keySet()) {
                    getAcademicPlanService().deletePlanItem(planItemIdToRemove,
                            getUserSessionHelper().makeContextInfoInstance());
                }
            }

        } catch (Exception e) {
            return doOperationFailedError(form, "Could not delete plan item", e);
        }

        //  Create events before updating the plan item.
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> originalRemoveEvents = makeRemoveEvent(planItem,
                courseDetails, form, null);
        //  Save the source ATP ID to create credit total updates later.
        String originalAtpId = planItem.getPlanPeriods().get(0);

        //  Do validations.
        //  Validate: Plan Size exceeded.
        LearningPlan learningPlan = null;
        boolean hasCapacity = false;
        try {
            learningPlan = getPlanHelper().getLearningPlan(getUserSessionHelper().getStudentId());
            hasCapacity = isAtpHasCapacity(getPlanHelper().getLearningPlan(getUserSessionHelper().getStudentId()),
                    newAtpId, planItem.getTypeKey());
        } catch (RuntimeException e) {
            return doOperationFailedError(form, "Could not validate capacity for new plan item.", e);
        }
        if (!hasCapacity) {
            return doPlanCapacityExceededError(form, planItem.getTypeKey());
        }

        //  Validate: Adding to historical term.
        if (!AtpHelper.isAtpSetToPlanning(newAtpId)) {
            return doCannotChangeHistoryError(form);
        }

        //  Update the plan item.
        planItem.setPlanPeriods(Arrays.asList(newAtpId));
        //  Changing types not current supported in this operation.
        //planItem.setTypeKey(newType);

        try {
            getAcademicPlanService().updatePlanItem(planItem.getId(), planItem,
                    getUserSessionHelper().makeContextInfoInstance());
        } catch (Exception e) {
            return doOperationFailedError(form, "Could not udpate plan item.", e);
        }

        //  Set the status of the request for the UI.
        form.setRequestStatus(PlanForm.REQUEST_STATUS.SUCCESS);

        //  Make Javascript UI events (delete, add, update credits).
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();
        /*Recommended item updated to accepted if a course or placeholder added from recommended dialog
            or only for courses added, moved or copied from other qtr's*/
        if (PlanConstants.COURSE_TYPE.equals(planItem.getRefObjectType())) {
            updateRecommendedItem(planItem, learningPlan, events);
        }

        //  Add events generated for the plan item before it was updated.
        events.putAll(originalRemoveEvents);
        //  Create update total credits on source ATP.
        events.putAll(makeUpdateTotalCreditsEvent(originalAtpId,
                PlanConstants.JS_EVENT_NAME.UPDATE_OLD_TERM_TOTAL_CREDITS));

        try {
            events.putAll(makeAddUpdateEvent(planItem, courseDetails, form, false));
        } catch (RuntimeException e) {
            return doOperationFailedError(form, "Unable to create add event.", e);
        }
        events.putAll(
                makeUpdateTotalCreditsEvent(newAtpId, PlanConstants.JS_EVENT_NAME.UPDATE_NEW_TERM_TOTAL_CREDITS));

        form.setJavascriptEvents(events);

        String link = makeLinkToAtp(newAtpId, AtpHelper.atpIdToTermName(newAtpId));
        String[] params = { link };
        return doPlanActionSuccess(form, PlanConstants.SUCCESS_KEY_PLANNED_ITEM_MOVED, params);
    }

    /**
     * Copy a course planned to requested term
     * And also used for adding the Recommend Item from recommended Dialog
     *
     * @param form
     * @param result
     * @param httprequest
     * @param httpresponse
     * @return
     */
    @RequestMapping(params = "methodToCall=copyPlannedCourse")
    public ModelAndView copyPlannedCourse(@ModelAttribute("KualiForm") PlanForm form, BindingResult result,
            HttpServletRequest httprequest, HttpServletResponse httpresponse) {
        if (getUserSessionHelper().isAdviser()) {
            return doAdviserAccessError(form, "Adviser Access Denied", null);
        }
        /**
         * This method needs a Plan Item ID and an ATP ID.
         */
        String planItemId = form.getPlanItemId();
        if (StringUtils.isEmpty(planItemId)) {
            return doOperationFailedError(form, "Plan Item ID was missing.", null);
        }

        // validation of Year and Term will happen in the service validation methods.
        if (StringUtils.isEmpty(form.getAtpId())) {
            return doOperationFailedError(form, "Term Year value missing", null);
        }

        String newAtpId = form.getAtpId();

        //  Can't validate further up because the new ATP ID can be "other".
        if (!AtpHelper.isAtpIdFormatValid(newAtpId)) {
            return doOperationFailedError(form, String.format("ATP ID [%s] was not formatted properly.", newAtpId),
                    null);
        }

        PlanItemInfo planItem = null;
        try {
            // First load the plan item and retrieve the courseId
            planItem = getAcademicPlanService().getPlanItem(planItemId, PlanConstants.CONTEXT_INFO);
        } catch (DoesNotExistException e) {
            return doPageRefreshError(form, "PlanItem with Id:" + planItemId + " doesnot exist", e);
        } catch (Exception e) {
            return doOperationFailedError(form, "Could not fetch plan item.", e);
        }

        if (planItem == null) {
            return doOperationFailedError(form, String.format("Could not fetch plan item."), null);
        }
        /*isRecommended --> true (we are copying the course from recommended section of the qtr to planned section of same qtr)*/
        String planType = form.isRecommended() ? PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED
                : planItem.getTypeKey();

        boolean isCrossListedCourse = false;
        String credit = null;
        //  Lookup course details as they will be needed for errors.
        CourseSummaryDetails courseDetails = null;
        if (!isPlaceHolderType(planItem.getRefObjectType())) {
            try {
                String crossListedCourse = getPlanHelper().getCrossListedCourse(planItem.getAttributes());
                isCrossListedCourse = !StringUtils.isEmpty(crossListedCourse);
                courseDetails = getCourseDetailsInquiryService()
                        .retrieveCourseSummaryByIdAndCd(planItem.getRefObjectId(), crossListedCourse);
            } catch (Exception e) {
                return doOperationFailedError(form, "Unable to retrieve Course Details.", e);
            }
        } else {
            courseDetails = new CourseSummaryDetails();
            if (planItem.getCredit() != null) {
                credit = String.valueOf(planItem.getCredit().intValue());
            }
        }

        //  Make sure there isn't a plan item for the same course id in the destination ATP.
        // unless maybe it's a placeholder? could go either way, but assume user actually wants additional placeholder
        if (!isPlaceHolderType(planItem.getRefObjectType())) {
            PlanItemInfo existingPlanItem = null;
            try {
                existingPlanItem = getPlanHelper().getPlannedOrBackupPlanItem(planItem.getRefObjectId(),
                        getPlanHelper().getCrossListedCourse(planItem.getAttributes()), newAtpId);
            } catch (RuntimeException e) {
                return doOperationFailedError(form, "Query for existing plan item failed.", e);
            }

            if (existingPlanItem != null) {
                String[] params = { courseDetails.getCode(), AtpHelper.atpIdToTermName(newAtpId) };
                return doErrorPage(form, PlanConstants.ERROR_KEY_PLANNED_ITEM_ALREADY_EXISTS, params);
            }
        }

        //  Do validations.
        //  Validate: Plan Size exceeded.
        boolean hasCapacity = false;
        LearningPlan learningPlan = null;
        try {
            learningPlan = getPlanHelper().getLearningPlan(getUserSessionHelper().getStudentId());
            hasCapacity = isAtpHasCapacity(learningPlan, newAtpId, planType);
        } catch (RuntimeException e) {
            return doOperationFailedError(form, "Could not validate capacity for new plan item.", e);
        }
        if (!hasCapacity) {
            return doPlanCapacityExceededError(form, planType);
        }

        //  Validate: Adding to historical term.
        if (!AtpHelper.isAtpSetToPlanning(newAtpId)) {
            return doCannotChangeHistoryError(form);
        }

        //  Update the plan item.
        planItem.setPlanPeriods(Arrays.asList(newAtpId));
        //  Do not allow diagonal moves .
        //planItem.setTypeKey(newType);

        //  Create the map of javascript event(s) that should be thrown in the UI.
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();

        PlanItemInfo planItemCopy = null;
        try {
            String note = form.isRecommended() ? null : planItem.getDescr().getPlain();
            planItemCopy = addPlanItem(learningPlan, planItem.getRefObjectId(), planItem.getRefObjectType(),
                    newAtpId, planType, note, credit, isCrossListedCourse ? courseDetails.getCode() : null);
            /*Recommended item updated to accepted if a course or placeholder added from recommended dialog
            or only for courses added, moved or copied from other qtr's*/
            if (form.isRecommended() || PlanConstants.COURSE_TYPE.equals(planItemCopy.getRefObjectType())) {
                updateRecommendedItem(planItem, learningPlan, events);
            }
        } catch (DuplicateEntryException e) {
            return doDuplicatePlanItem(form, newAtpId, courseDetails.getCode());
        } catch (Exception e) {
            return doOperationFailedError(form, "Unable to add plan item.", e);
        }

        //  Set the status of the request for the UI.
        form.setRequestStatus(PlanForm.REQUEST_STATUS.SUCCESS);

        try {
            events.putAll(makeAddUpdateEvent(planItemCopy, courseDetails, form, false));
        } catch (RuntimeException e) {
            return doOperationFailedError(form, "Unable to create add event.", e);
        }

        events.putAll(
                makeUpdateTotalCreditsEvent(newAtpId, PlanConstants.JS_EVENT_NAME.UPDATE_NEW_TERM_TOTAL_CREDITS));

        //  Populate the form.
        form.setJavascriptEvents(events);

        String link = makeLinkToAtp(newAtpId, AtpHelper.atpIdToTermName(newAtpId));
        String[] params = { link };
        return doPlanActionSuccess(form, PlanConstants.SUCCESS_KEY_PLANNED_ITEM_COPIED, params);
    }

    /**
     * Adds a course to plan for requested academic term
     *
     * @param form
     * @param result
     * @param httprequest
     * @param httpresponse
     * @return
     */
    @RequestMapping(params = "methodToCall=addUpdatePlanItem")
    public ModelAndView addUpdatePlanItem(@ModelAttribute("KualiForm") PlanForm form, BindingResult result,
            HttpServletRequest httprequest, HttpServletResponse httpresponse) {
        if (getUserSessionHelper().isAdviser()) {
            return doAdviserAccessError(form, "Adviser Access Denied", null);
        }
        /* Should the course be type 'planned' or 'backup'. Default to planned.*/
        String newType = form.isBackup() ? PlanConstants.LEARNING_PLAN_ITEM_TYPE_BACKUP
                : PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED;
        return addUpdateItem(form, newType);
    }

    /**
     * Adds a course to plan for requested academic term
     *
     * @param form
     * @param result
     * @param httprequest
     * @param httpresponse
     * @return
     */
    @RequestMapping(params = "methodToCall=addRecommendedPlanItem")
    public ModelAndView addRecommendedPlanItem(@ModelAttribute("KualiForm") PlanForm form, BindingResult result,
            HttpServletRequest httprequest, HttpServletResponse httpresponse) {
        if (!getUserSessionHelper().isAdviser()) {
            return doStudentAccessError(form, "Student Access Denied", null);
        }
        return addUpdateItem(form, PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED);
    }

    /**
     * Plan Access for Adviser is updated in this method
     *
     * @param form
     * @param result
     * @param httprequest
     * @param httpresponse
     * @return
     */

    @RequestMapping(params = "methodToCall=planAccess")
    public ModelAndView planAccess(@ModelAttribute("KualiForm") PlanForm form, BindingResult result,
            HttpServletRequest httprequest, HttpServletResponse httpresponse) {
        if (getUserSessionHelper().isAdviser()) {
            String[] params = {};
            return doErrorPage(form, PlanConstants.ERROR_KEY_ADVISER_ACCESS, params);
        }
        List<LearningPlanInfo> plan = new ArrayList<LearningPlanInfo>();
        try {
            String studentId = getUserSessionHelper().getStudentId();
            // Synchronized is used to ensure only one learning plan is created for a given student Id
            synchronized (studentId) {
                plan = getAcademicPlanService().getLearningPlansForStudentByType(studentId,
                        PlanConstants.LEARNING_PLAN_TYPE_PLAN, PlanConstants.CONTEXT_INFO);
                if (!CollectionUtils.isEmpty(plan)) {
                    LearningPlanInfo learningPlan = plan.get(0);
                    if (!learningPlan.getShared().toString().equalsIgnoreCase(form.getEnableAdviserView())) {
                        if (form.getEnableAdviserView()
                                .equalsIgnoreCase(PlanConstants.LEARNING_PLAN_ITEM_SHARED_TRUE_KEY)) {
                            learningPlan.setShared(true);
                        } else {
                            learningPlan.setShared(false);
                        }
                        learningPlan.setStateKey(PlanConstants.LEARNING_PLAN_ACTIVE_STATE_KEY);
                        getAcademicPlanService().updateLearningPlan(learningPlan.getId(), learningPlan,
                                PlanConstants.CONTEXT_INFO);
                    }
                } else {
                    LearningPlanInfo planInfo = new LearningPlanInfo();
                    planInfo.setTypeKey(PlanConstants.LEARNING_PLAN_TYPE_PLAN);
                    RichTextInfo rti = new RichTextInfo();
                    rti.setFormatted("");
                    rti.setPlain("");
                    if (form.getEnableAdviserView()
                            .equalsIgnoreCase(PlanConstants.LEARNING_PLAN_ITEM_SHARED_TRUE_KEY)) {
                        planInfo.setShared(true);
                    } else {
                        planInfo.setShared(false);
                    }
                    planInfo.setDescr(rti);
                    planInfo.setStudentId(studentId);
                    planInfo.setStateKey(PlanConstants.LEARNING_PLAN_ACTIVE_STATE_KEY);
                    planInfo.setMeta(new MetaInfo());

                    ContextInfo context = new ContextInfo();
                    context.setPrincipalId(studentId);
                    getAcademicPlanService().createLearningPlan(planInfo, context);
                }
            }
        } catch (Exception e) {
            return doOperationFailedError(form, "Query for default learning plan failed.", e);
        }

        return getUIFModelAndView(form);

    }

    /**
     * Update PlanItem Note
     *
     * @param form
     * @param result
     * @param httprequest
     * @param httpresponse
     * @return
     */
    @RequestMapping(params = "methodToCall=updateNote")
    public ModelAndView updateNote(@ModelAttribute("KualiForm") PlanForm form, BindingResult result,
            HttpServletRequest httprequest, HttpServletResponse httpresponse) {
        PlanItemInfo planItemInfo = null;
        CourseSummaryDetails courseSummaryDetails = null;
        String atpId = null;
        if (form.getPlanItemId() != null) {

            try {
                planItemInfo = getAcademicPlanService().getPlanItem(form.getPlanItemId(),
                        PlanConstants.CONTEXT_INFO);
                if (PlanConstants.PLACE_HOLDER_OTHER_CODE.equals(planItemInfo.getRefObjectId())
                        && !hasText(form.getNote())) {
                    return doErrorPage(form, "Note required", PlanConstants.NOTE_REQUIRED, new String[] {}, null);
                }
                planItemInfo.getDescr().setPlain(form.getNote());
                planItemInfo.getDescr().setFormatted(form.getNote());
                planItemInfo = getAcademicPlanService().updatePlanItem(form.getPlanItemId(), planItemInfo,
                        PlanConstants.CONTEXT_INFO);
                if (!isPlaceHolderType(planItemInfo.getRefObjectType())) {
                    courseSummaryDetails = getVersionVerifiedCourseDetails(planItemInfo.getRefObjectId(), null);
                    atpId = planItemInfo.getPlanPeriods().get(0);
                }

            } catch (DoesNotExistException e) {
                return doPageRefreshError(form, "PlanItem with Id:" + form.getPlanItemId() + " doesnot exist", e);
            } catch (Exception e) {

                return doOperationFailedError(form, "Failed to get PlanItem for planItemId " + form.getPlanItemId(),
                        e);

            }

        } else {

            return doOperationFailedError(form, "Failed to update note", null);

        }

        //  Create events
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();

        events.putAll(makeAddUpdateEvent(planItemInfo, courseSummaryDetails, form, true));

        if (courseSummaryDetails != null && hasText(courseSummaryDetails.getCode())) {
            addStatusAlertEvents(courseSummaryDetails.getCode(), atpId, events, true);
        }

        form.setRequestStatus(PlanForm.REQUEST_STATUS.SUCCESS);
        form.setJavascriptEvents(events);

        return doPlanActionSuccess(form, PlanConstants.SUCCESS_KEY_UPDATED_ITEM, new String[] {});
    }

    /**
     * Method used to add and update planItems
     * Adds and updates a course, course placeHolder  general placeHolder planItems
     * Adds a recommended Course plan Item
     *
     * @param form
     * @param newType
     * @return
     */
    private ModelAndView addUpdateItem(PlanForm form, String newType) {
        /*
        * ************************************************************************************************************
        *                                  QuickAdd a course or placeholder to Plan
        * ************************************************************************************************************
        */

        boolean addCourse = true;
        boolean addPlaceHolder = false;
        boolean addPrimaryCourse = false;
        boolean addSecondaryCourse = false;
        boolean isCrossListedCourse = false;
        boolean isRecommendedItem = PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED.equals(newType);
        CourseSummaryDetails courseDetails = null;
        String studentId = getUserSessionHelper().getStudentId();

        String planItemId = form.getPlanItemId();

        String quickAddType = form.getType();
        String courseCd = form.getCourseCd();
        String placeHolderId = null;
        String placeHolderType = null;
        String placeHolderCd = null;
        String placeHolderTitle = null;
        if (hasText(form.getGeneralPlaceholder())) {
            if (PlanConstants.DEFAULT_KEY.equals(form.getGeneralPlaceholder())) {
                return doErrorPage(form, "Select a placeHolder", PlanConstants.DEFAULT_PLACEHOLDER_ERROR,
                        new String[] {}, null);
            }
            String[] placeHolder = form.getGeneralPlaceholder().split(PlanConstants.CODE_KEY_SEPARATOR);
            placeHolderId = placeHolder[0];
            placeHolderType = placeHolder[1];
            placeHolderCd = EnumerationHelper.getEnumAbbrValForCodeByType(placeHolderId, placeHolderType);
            placeHolderTitle = EnumerationHelper.getEnumValueForCodeByType(placeHolderId, placeHolderType);
        }

        if (PlanConstants.ADD_DIALOG_PAGE.equals(form.getPageId())
                || PlanConstants.ADD_RECOMMENDED_DIALOG_PAGE.equals(form.getPageId())
                || PlanConstants.RECOMMENDED_DIALOG_PAGE.equals(form.getPageId())) {
            if (hasText(courseCd) || hasText(placeHolderId)) {
                /*Updating the plan item from quickAdd*/
                if (hasText(planItemId)) {
                    return updatePlaceHolder(form, planItemId);
                }

                if (PlanConstants.GENERAL_TYPE.equals(quickAddType)) {

                    addPlaceHolder = true;

                    if (PlanConstants.PLACE_HOLDER_OTHER_CODE.equals(placeHolderId) && !hasText(form.getNote())) {
                        return doErrorPage(form, "Note required", PlanConstants.NOTE_REQUIRED, new String[] {},
                                null);
                    }

                } else {

                    DeconstructedCourseCode courseCode = getCourseHelper().getCourseDivisionAndNumber(courseCd);

                    if (courseCode.getSubject() != null && courseCode.getNumber() != null) {

                        String subject = courseCode.getSubject();
                        String number = courseCode.getNumber();

                        /*Differentiating normal course with course level PlaceHolder*/
                        if (number.matches(PlanConstants.COURSE_PLACEHOLDER_REGEX)) {
                            // validate the subject
                            HashMap<String, String> divisionMap = getCourseHelper().fetchCourseDivisions();
                            ArrayList<String> divisions = new ArrayList<String>();
                            getCourseHelper().extractDivisions(divisionMap, subject, divisions, false);
                            if (divisions.size() > 0) {
                                // subject was found, hence is valid
                                Map<String, String> subjectAreas = OrgHelper.getTrimmedSubjectAreas();
                                addPlaceHolder = true;
                                placeHolderType = PlanConstants.PLACE_HOLDER_TYPE_COURSE_LEVEL;
                                placeHolderId = String.format("%s %s", divisions.get(0).trim(), number);
                                placeHolderCd = placeHolderId;
                                String subjectTitle = subjectAreas.get(courseCode.getSubject());
                                String subjectLevel = courseCode.getNumber().toUpperCase().replace(
                                        CourseSearchConstants.COURSE_LEVEL_XX,
                                        CourseSearchConstants.COURSE_LEVEL_ZERO);
                                if (!getCourseHelper().isValidCourseLevel(courseCode.getSubject(), subjectLevel)) {
                                    return doErrorPage(form, "Course level not found",
                                            PlanConstants.COURSE_LEVEL_NOT_FOUND, new String[] { courseCd }, null);
                                }
                                placeHolderTitle = String.format("%s %s level", subjectTitle, subjectLevel);
                            } else {
                                return doErrorPage(form, "Curriculum is invalid", PlanConstants.CURRIC_NOT_FOUND,
                                        new String[] { subject }, null);
                            }

                        } else {

                            String courseId = getCourseHelper().getCourseId(subject, number);
                            form.setCourseId(courseId);
                            form.setCode(courseCd);

                        }

                    }

                    if (form.getCourseId() == null && !addPlaceHolder) {
                        return doErrorPage(form, "Course not found", PlanConstants.COURSE_NOT_FOUND,
                                new String[] { courseCd }, null);
                    }

                }
            } else {
                return doErrorPage(form, "Empty Search", PlanConstants.EMPTY_SEARCH, new String[] { courseCd },
                        null);
            }
        }

        String code = form.getCode();

        if (!addPlaceHolder) {

            /* This method needs a Course ID and an ATP ID when Planning a course.*/
            String courseId = form.getCourseId();

            if (StringUtils.isEmpty(courseId)) {
                return doOperationFailedError(form, "Course ID was missing.", null);
            }

            if (StringUtils.isEmpty(code)) {
                return doOperationFailedError(form, "Course CD was missing.", null);
            }

            CourseInfo courseInfo = getCourseHelper().getCourseInfo(courseId);
            try {
                isCrossListedCourse = getCourseHelper().isCrossListedCourse(courseInfo, code);
            } catch (DoesNotExistException e) {
                return doErrorPage(form, "Course not found", PlanConstants.COURSE_NOT_FOUND, new String[] { code },
                        null);
            }

            courseDetails = getVersionVerifiedCourseDetails(courseId, code);

            if (courseDetails == null) {
                return doOperationFailedError(form, "Unable to retrieve Course Details for courseId " + courseId,
                        null);
            }
        }

        String newAtpId = form.getAtpId();

        /*Term is required for this method to complete*/
        if (!hasText(newAtpId)) {
            return doOperationFailedError(form, "Missing Term to add course to plan", null);
        }

        /*Term format validation*/
        if (!AtpHelper.isAtpIdFormatValid(newAtpId)) {
            return doOperationFailedError(form, String.format("ATP ID [%s] was not formatted properly.", newAtpId),
                    null);
        }

        /*Historical term validation*/
        if (!AtpHelper.isAtpSetToPlanning(newAtpId)) {
            return doCannotChangeHistoryError(form);
        }

        LearningPlan plan = getSynchronizedLearningPlan(studentId);
        if (plan == null) {
            return doOperationFailedError(form, "Unable to create/retrieve learning plan.", null);
        }

        /*
        * ************************************************************************************************************
        * Process the activities to know if course or primary activity or secondary activity or all need to be added
        * to the Plan
        *
        * Planning a course --> addCourse will be true.
        * Planning a primary activity and course not planned --> addCourse will be true and add primary will be true.
        * Planning a secondary activity and primary not planned and course not planned --> addCourse will be true and add
        * primary will be true and secondary will be true.
        * ************************************************************************************************************
        */

        if (form.getSectionCode() != null) {
            List<ActivityOfferingItem> activityOfferings = getCourseDetailsInquiryService()
                    .getActivityOfferingItemsByIdAndCd(courseDetails.getCourseId(),
                            isCrossListedCourse ? courseDetails.getCode() : null, form.getAtpId());
            Map<String, String> primaryActivityToRegCode = new HashMap<String, String>();
            /*Populate the primary and secondary flags*/
            for (ActivityOfferingItem activityOfferingItem : activityOfferings) {
                if (activityOfferingItem.isPrimary() && !form.isPrimary()) {
                    primaryActivityToRegCode.put(activityOfferingItem.getCode(),
                            activityOfferingItem.getRegistrationCode());
                }
                if (activityOfferingItem.getCode().equalsIgnoreCase(form.getSectionCode())) {
                    if (activityOfferingItem.isPrimary()) {
                        PlanItemInfo coursePlanItem = getPlanHelper().getPlannedOrBackupPlanItem(
                                courseDetails.getVersionIndependentId(),
                                isCrossListedCourse ? courseDetails.getCode() : null, form.getAtpId());
                        if (coursePlanItem != null) {
                            addCourse = false;
                        }
                        addPrimaryCourse = true;
                        form.setPrimarySectionCode(activityOfferingItem.getCode());
                        form.setPrimaryRegistrationCode(activityOfferingItem.getRegistrationCode());
                        break;
                    } else {
                        PlanItemInfo primaryPlanItem = getPlanHelper().getPlannedOrBackupPlanItem(
                                activityOfferingItem.getPrimaryActivityOfferingId(), null, form.getAtpId());
                        if (primaryPlanItem != null) {
                            addCourse = false;
                        } else {
                            addPrimaryCourse = true;
                            form.setPrimarySectionCode(getCourseHelper()
                                    .getCodeFromActivityId(activityOfferingItem.getPrimaryActivityOfferingId()));
                            form.setPrimaryRegistrationCode(
                                    primaryActivityToRegCode.get(form.getPrimarySectionCode()));
                            PlanItemInfo coursePlanItem = getPlanHelper().getPlannedOrBackupPlanItem(
                                    courseDetails.getVersionIndependentId(),
                                    isCrossListedCourse ? courseDetails.getCode() : null, form.getAtpId());
                            if (coursePlanItem != null) {
                                addCourse = false;
                            }
                        }
                        addSecondaryCourse = true;
                        break;
                    }
                }
            }
        }

        /*Plan capacity validation.*/
        if (addCourse || addPlaceHolder) {
            boolean hasCapacity = false;
            if (isRecommendedItem) {
                /*Since recommendations has no limit*/
                hasCapacity = true;
            } else {
                try {
                    hasCapacity = isAtpHasCapacity(plan, newAtpId, newType);
                } catch (RuntimeException e) {
                    return doOperationFailedError(form, "Could not validate capacity for new plan item.", e);
                }
            }

            if (!hasCapacity) {
                return doPlanCapacityExceededError(form, newType);
            }
        }

        /*
        * ************************************************************************************************************
        *                                  Adding a course to Plan
        * ************************************************************************************************************
        */

        PlanItemInfo planItem = null;
        /*PlanItems for sections*/
        PlanItemInfo primaryPlanItem = null;
        PlanItemInfo secondaryPlanItem = null;

        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> wishlistEvents = null;

        if (!addPlaceHolder) {
            if (!isRecommendedItem && addCourse) {
                //  See if a wishList item exists for the course. If so, then update it. Otherwise create a new plan item.
                planItem = getWishlistPlanItem(courseDetails.getVersionIndependentId(),
                        isCrossListedCourse ? code : null);
            }
            if (planItem == null && addCourse) {
                try {
                    // Don't allow duplicate recommended items.
                    if (PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED.equals(newType)
                            && getPlanHelper().getPlanItemByAtpAndType(plan.getId(),
                                    courseDetails.getVersionIndependentId(), newAtpId, newType,
                                    isCrossListedCourse ? code : null) != null) {
                        return doDuplicateRecommendedItem(form, newAtpId, courseDetails.getCode());
                    }

                    planItem = addPlanItem(plan, courseDetails.getVersionIndependentId(), PlanConstants.COURSE_TYPE,
                            newAtpId, newType, form.getNote(), null, isCrossListedCourse ? code : null);

                    if (isRecommendedItem) {
                        sendRecommendationNotification(AtpHelper.atpIdToTermName(newAtpId), courseDetails.getCode(),
                                form.getNote(), null, false);
                    }

                } catch (DuplicateEntryException e) {
                    return doDuplicatePlanItem(form, newAtpId, courseDetails.getCode());
                } catch (Exception e) {
                    return doOperationFailedError(form, "Unable to add plan item.", e);
                }
            } else if (planItem != null && addCourse) {

                //  Check for duplicates since addPlanItem isn't being called.
                if (addCourse && isDuplicate(newAtpId, courseDetails.getVersionIndependentId(), newType,
                        isCrossListedCourse ? courseDetails.getCode() : null)) {
                    return doDuplicatePlanItem(form, newAtpId, courseDetails.getCode());
                }

                //  Create wishList events before updating the plan item.
                wishlistEvents = makeRemoveEvent(planItem, courseDetails, form, null);
                planItem.setTypeKey(newType);
                planItem.setPlanPeriods(Arrays.asList(newAtpId));
                String note = hasText(form.getNote()) ? form.getNote() : "";
                planItem.setDescr(new RichTextInfo(note, note));

                try {
                    String oldPlanItemId = planItem.getId();
                    //New PlanItem
                    planItem = getAcademicPlanService().createPlanItem(planItem,
                            getUserSessionHelper().makeContextInfoInstance());
                    //delete old wishList item
                    getAcademicPlanService().deletePlanItem(oldPlanItemId,
                            getUserSessionHelper().makeContextInfoInstance());

                } catch (Exception e) {
                    return doOperationFailedError(form, "Could not add new plan item.", e);
                }
            }

            /*
            * *************************************************************************************************************
            *                             Adding activities to Plan
            * *************************************************************************************************************
            */

            if (addPrimaryCourse && form.getPrimarySectionCode() != null) {
                String refObjId = getCourseHelper().buildActivityRefObjId(newAtpId, courseDetails.getSubjectArea(),
                        courseDetails.getCourseNumber(), form.getPrimarySectionCode());
                primaryPlanItem = addPlanItem(plan, refObjId, PlanConstants.SECTION_TYPE, newAtpId, newType,
                        form.getNote(), null, null);
                form.setPrimaryPlanItemId(primaryPlanItem.getId());

            }

            if (addSecondaryCourse && form.getSectionCode() != null) {
                String refObjId = getCourseHelper().buildActivityRefObjId(newAtpId, courseDetails.getSubjectArea(),
                        courseDetails.getCourseNumber(), form.getSectionCode());
                secondaryPlanItem = addPlanItem(plan, refObjId, PlanConstants.SECTION_TYPE, newAtpId, newType,
                        form.getNote(), null, null);

            }

        }
        /*
        * *************************************************************************************************************
        *                   Adding placeholders to the plan
        * *************************************************************************************************************
        */
        else {
            try {

                planItem = addPlanItem(plan, placeHolderId, placeHolderType, newAtpId, newType, form.getNote(),
                        form.getCredit(), null);

                if (isRecommendedItem) {
                    /*Creating a message*/
                    sendRecommendationNotification(AtpHelper.atpIdToTermName(newAtpId), placeHolderCd,
                            form.getNote(), placeHolderTitle, false);
                }

            } catch (DuplicateEntryException e) {
                return doDuplicatePlanItem(form, newAtpId, placeHolderCd != null ? placeHolderCd : courseCd);
            } catch (Exception e) {
                return doOperationFailedError(form, "Unable to add place Holder item of type." + placeHolderType,
                        e);
            }
        }

        /*
        * *************************************************************************************************************
        *                   Adding events to the form
        * *************************************************************************************************************
        */

        /*Create the map of javascript event(s) that should be thrown in the UI.*/
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();

        /*If a wishList item was clobbered then generate Javascript events.*/
        if (wishlistEvents != null) {
            events.putAll(wishlistEvents);
        }
        String plannedTerm = null;
        try {
            if (planItem != null) {
                plannedTerm = planItem.getPlanPeriods().get(0);
                events.putAll(makeAddUpdateEvent(planItem, courseDetails, form, false));
                /*Recommended item updated to accepted if a course or placeholder added from recommended dialog
                or only for courses added, moved or copied from other qtr's*/
                if (!PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED.equals(planItem.getTypeKey())
                        && PlanConstants.COURSE_TYPE.equals(planItem.getRefObjectType())) {
                    updateRecommendedItem(planItem, plan, events);
                }
            }
            if (primaryPlanItem != null) {
                plannedTerm = primaryPlanItem.getPlanPeriods().get(0);
                events.putAll(makeAddUpdateEvent(primaryPlanItem, courseDetails, form, false));
            }
            if (secondaryPlanItem != null) {
                plannedTerm = secondaryPlanItem.getPlanPeriods().get(0);
                events.putAll(makeAddUpdateEvent(secondaryPlanItem, courseDetails, form, false));
            }
        } catch (RuntimeException e) {
            return doOperationFailedError(form, "Unable to create add event.", e);
        }

        events.putAll(makeUpdateTotalCreditsEvent(plannedTerm,
                PlanConstants.JS_EVENT_NAME.UPDATE_NEW_TERM_TOTAL_CREDITS));

        form.setJavascriptEvents(events);

        String[] params = {};
        if (planItem != null) {
            params = new String[] { makeLinkToAtp(planItem.getPlanPeriods().get(0),
                    AtpHelper.atpIdToTermName(planItem.getPlanPeriods().get(0))) };
        } else if (primaryPlanItem != null) {
            params = new String[] { makeLinkToAtp(primaryPlanItem.getPlanPeriods().get(0),
                    AtpHelper.atpIdToTermName(primaryPlanItem.getPlanPeriods().get(0))) };
        } else if (secondaryPlanItem != null) {
            params = new String[] { makeLinkToAtp(secondaryPlanItem.getPlanPeriods().get(0),
                    AtpHelper.atpIdToTermName(secondaryPlanItem.getPlanPeriods().get(0))) };
        }

        return doPlanActionSuccess(form, PlanConstants.SUCCESS_KEY_PLANNED_ITEM_ADDED, params);
    }

    /**
     * Recommended Item updated to accepted if a course or placeholder added from recommended dialog
     * or only for courses added, moved or copied from other qtr's
     * Also adds the delete recommendation event to the form events.
     *
     * @param planItem
     * @param learningPlan
     * @param events
     */
    private void updateRecommendedItem(PlanItemInfo planItem, LearningPlan learningPlan,
            Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events) {
        /*Adding the recommended events if a recommended item exists for the same refObjId and atp*/
        PlanItemInfo recommendedItem = getPlanHelper().getPlanItemByAtpAndType(learningPlan.getId(),
                planItem.getRefObjectId(), planItem.getPlanPeriods().get(0),
                PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED,
                getPlanHelper().getCrossListedCourse(planItem.getAttributes()));
        if (recommendedItem != null && hasText(recommendedItem.getId())) {
            makeRecommendDeleteEvent(recommendedItem, events);
        }
        try {
            recommendedItem.setStateKey(PlanConstants.LEARNING_PLAN_ITEM_ACCEPTED_STATE_KEY);
            getAcademicPlanService().updatePlanItem(recommendedItem.getId(), recommendedItem,
                    PlanConstants.CONTEXT_INFO);
        } catch (Exception e) {
            logger.error("Unable to update the recommended Item state", e);
        }

    }

    /**
     * Add events to Delete the recommended item once it is planned
     *
     * @param planItem
     * @param events
     */
    private void makeRecommendDeleteEvent(PlanItemInfo planItem,
            Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("planItemId", planItem.getId());
        params.put("planItemType", formatTypeKey(planItem.getTypeKey()));
        params.put("atpId", planItem.getPlanPeriods().get(0));
        events.put(PlanConstants.JS_EVENT_NAME.RECOMMENDED_ITEM_UPDATED, params);
    }

    /**
     * Creates a message for Student about recommended course from adviser
     * Sends a email notification to student about the recommended course from adviser
     *
     * @param term
     * @param courseCd
     * @param note
     * @param removed
     */
    private void sendRecommendationNotification(String term, String courseCd, String note, String title,
            boolean removed) {

        Properties pro = new Properties();
        InputStream file = getClass().getResourceAsStream(PlanConstants.PROPERTIES_FILE_PATH);
        try {
            pro.load(file);
        } catch (Exception e) {
            logger.error("Could not find the properties file" + e);
        }

        if (!CollectionUtils.isEmpty(pro)) {
            String adviserName = getUserSessionHelper()
                    .getCapitalizedName(getUserSessionHelper().getCurrentUserId());
            note = hasText(note)
                    ? String.format("'%s'", WordUtils.wrap(note.trim().replace("\n", "<br/>"), 80, "<br/>", true))
                    : "";
            String dateAdded = DateFormatHelper
                    .getDateFomatted(new java.sql.Date(System.currentTimeMillis()).toString());
            String planLink = makeLinkToAtp(AtpHelper.termToYearTerm(term).toATP(), "visit your plan page");
            String code = courseCd;
            courseCd = title != null ? (title.equals(courseCd) ? String.format("'%s' placeholder", courseCd)
                    : String.format("%s '%s' placeholder", courseCd, title)) : courseCd;

            /*Creating a new Message from Adviser to student*/
            String emailSubject = "";
            String subject = "";
            String message = "";
            String emailMessage = "";
            if (!removed) {
                subject = String.format((String) pro.get(PlanConstants.ADD_RECOMMEND_NOTIFICATION_MESSAGE_SUBJECT),
                        code);
                emailSubject = String.format((String) pro.get(PlanConstants.ADD_RECOMMEND_NOTIFICATION_SUBJECT),
                        adviserName);
                message = String.format((String) pro.get(PlanConstants.ADD_RECOMMEND_NOTIFICATION_BODY),
                        adviserName, courseCd, term, "\n" + note);
                emailMessage = String.format((String) pro.get(PlanConstants.ADD_RECOMMEND_NOTIFICATION_BODY),
                        adviserName, courseCd, term, "<br>" + note)
                        + String.format((String) pro.get(PlanConstants.ADD_RECOMMEND_NOTIFICATION_INFO), dateAdded,
                                planLink);
            } else {
                subject = String.format(
                        (String) pro.get(PlanConstants.REMOVED_RECOMMEND_NOTIFICATION_MESSAGE_SUBJECT), code);
                emailSubject = String.format((String) pro.get(PlanConstants.REMOVED_RECOMMEND_NOTIFICATION_SUBJECT),
                        adviserName);
                message = String.format((String) pro.get(PlanConstants.REMOVED_RECOMMEND_NOTIFICATION_INFO),
                        adviserName, courseCd, term, dateAdded, note);
                emailMessage = message;
            }

            try {
                getCommentHelper().createMessage(subject, message);
            } catch (Exception e) {
                logger.error("Error creating message for adviser recommended course", e);
            }

            /*Sending a email notification to student about the recommended course*/
            try {
                getCommentHelper().sendRecommendationEmailNotification(emailSubject, emailMessage);
            } catch (Exception e) {
                logger.error("Error sending message notification for adviser recommended course", e);
            }
        } else {
            logger.error("Could not find the properties file, failed to create and send message notification");
        }

    }

    /**
     * returns true if the reofbjtype is a placeholder
     *
     * @param refObjType
     * @return
     */
    private boolean isPlaceHolderType(String refObjType) {
        return PlanConstants.PLACE_HOLDER_TYPE_COURSE_LEVEL.equals(refObjType)
                || PlanConstants.PLACE_HOLDER_TYPE_GEN_ED.equals(refObjType)
                || PlanConstants.PLACE_HOLDER_TYPE.equals(refObjType);
    }

    /**
     * Updates the Placeholders or notes of a planItem
     *
     * @param form
     * @param planItemId
     * @return
     */
    private ModelAndView updatePlaceHolder(PlanForm form, String planItemId) {

        PlanItemInfo planItemInfo = null;
        CourseSummaryDetails courseSummaryDetails = null;
        try {
            planItemInfo = getAcademicPlanService().getPlanItem(planItemId, PlanConstants.CONTEXT_INFO);

            if (planItemInfo != null) {
                String atpId = planItemInfo.getPlanPeriods().get(0);
                String placeHolderId = null;
                String placeHolderType = null;
                String placeHolderCd = null;
                boolean creditUpdated = false;
                boolean isCrossListedCourse = false;

                /*General placeholder/ placeholder type and value update */
                if (hasText(form.getGeneralPlaceholder())) {
                    String[] placeHolder = form.getGeneralPlaceholder().split(PlanConstants.CODE_KEY_SEPARATOR);
                    placeHolderId = placeHolder[0];
                    placeHolderType = placeHolder[1];
                    placeHolderCd = EnumerationHelper.getEnumAbbrValForCodeByType(placeHolderId, placeHolderType);
                    if (PlanConstants.PLACE_HOLDER_OTHER_CODE.equals(placeHolderId) && !hasText(form.getNote())) {
                        return doErrorPage(form, "Note required", PlanConstants.NOTE_REQUIRED, new String[] {},
                                null);
                    }
                    planItemInfo.setRefObjectId(placeHolderId);
                    planItemInfo.setRefObjectType(placeHolderType);
                }

                /*Course level PlaceHolder type and value update*/
                if (hasText(form.getCourseCd())) {

                    DeconstructedCourseCode courseCode = getCourseHelper()
                            .getCourseDivisionAndNumber(form.getCourseCd());

                    if (courseCode.getSubject() != null && courseCode.getNumber() != null) {
                        String subject = courseCode.getSubject();
                        String number = courseCode.getNumber();
                        if (number.matches(PlanConstants.COURSE_PLACEHOLDER_REGEX)) {
                            // validate the subject
                            HashMap<String, String> divisionMap = getCourseHelper().fetchCourseDivisions();
                            ArrayList<String> divisions = new ArrayList<String>();
                            getCourseHelper().extractDivisions(divisionMap, subject, divisions, false);
                            if (divisions.size() > 0) {
                                planItemInfo
                                        .setRefObjectId(String.format("%s %s", divisions.get(0).trim(), number));
                                planItemInfo.setRefObjectType(PlanConstants.PLACE_HOLDER_TYPE_COURSE_LEVEL);
                                String subjectLevel = number.toUpperCase().replace(
                                        CourseSearchConstants.COURSE_LEVEL_XX,
                                        CourseSearchConstants.COURSE_LEVEL_ZERO);
                                if (!getCourseHelper().isValidCourseLevel(courseCode.getSubject(), subjectLevel)) {
                                    return doErrorPage(form, "Course level not found",
                                            PlanConstants.COURSE_LEVEL_NOT_FOUND,
                                            new String[] { form.getCourseCd() }, null);
                                }
                            } else {
                                return doErrorPage(form, "Curriculum is invalid", PlanConstants.CURRIC_NOT_FOUND,
                                        new String[] { subject }, null);
                            }

                        } else {
                            String planItemCourseCode = null;
                            if (PlanConstants.COURSE_TYPE.equals(planItemInfo.getRefObjectType())) {
                                CourseInfo planItemCourseInfo = getCourseHelper().getCourseInfoByIdAndCd(
                                        planItemInfo.getRefObjectId(),
                                        getPlanHelper().getCrossListedCourse(planItemInfo.getAttributes()));
                                planItemCourseCode = planItemCourseInfo.getCode();
                            }
                            String courseId = getCourseHelper().getCourseId(subject, number);
                            if (!hasText(courseId)) {
                                return doErrorPage(form, "Course not found", PlanConstants.COURSE_NOT_FOUND,
                                        new String[] { form.getCourseCd() }, null);
                            }
                            CourseInfo courseInfo = getCourseHelper().getCourseInfo(courseId);
                            try {
                                isCrossListedCourse = getCourseHelper().isCrossListedCourse(courseInfo,
                                        form.getCourseCd());
                            } catch (DoesNotExistException e) {
                                return doErrorPage(form, "Course not found", PlanConstants.COURSE_NOT_FOUND,
                                        new String[] { form.getCourseCd() }, null);
                            }
                            courseSummaryDetails = getCourseDetailsInquiryService().retrieveCourseSummaryByIdAndCd(
                                    courseId, isCrossListedCourse ? form.getCourseCd() : null);
                            String versionId = courseSummaryDetails.getVersionIndependentId();
                            //  Check for duplicates since addPlanItem isn't being called.
                            if ((planItemCourseCode == null
                                    || !courseSummaryDetails.getCode().equals(planItemCourseCode))
                                    && isDuplicate(atpId, versionId, planItemInfo.getTypeKey(),
                                            isCrossListedCourse ? form.getCourseCd() : null)) {
                                return doDuplicatePlanItem(form, atpId, courseSummaryDetails.getCode());
                            }
                            planItemInfo.setRefObjectId(versionId);
                            planItemInfo.setRefObjectType(PlanConstants.COURSE_TYPE);

                            /*If a crossListed course is being added in then courseCd needs to be added as dynamic attribute.
                             If a CrossListed course if being changed to regular course then the crossListed dynamic attribute should be removed*/
                            if (isCrossListedCourse) {
                                boolean added = false;
                                for (AttributeInfo attributeInfo : planItemInfo.getAttributes()) {
                                    if (PlanConstants.CROSS_LISTED_COURSE_ATTR_KEY.equals(attributeInfo.getKey())) {
                                        attributeInfo.setValue(form.getCourseCd());
                                        added = true;
                                        break;
                                    }
                                }
                                if (!added) {
                                    List<AttributeInfo> attributeInfos = planItemInfo.getAttributes();
                                    if (attributeInfos == null) {
                                        attributeInfos = new ArrayList<AttributeInfo>();
                                    }
                                    attributeInfos.add(new AttributeInfo(PlanConstants.CROSS_LISTED_COURSE_ATTR_KEY,
                                            form.getCourseCd()));
                                }
                            } else {
                                AttributeInfo attributeInfoToRemove = null;
                                for (AttributeInfo attributeInfo : planItemInfo.getAttributes()) {
                                    if (PlanConstants.CROSS_LISTED_COURSE_ATTR_KEY.equals(attributeInfo.getKey())) {
                                        attributeInfoToRemove = attributeInfo;
                                        break;
                                    }
                                }
                                if (attributeInfoToRemove != null) {
                                    planItemInfo.getAttributes().remove(attributeInfoToRemove);
                                }
                            }
                        }

                    } else {
                        return doErrorPage(form, "Course not found", PlanConstants.COURSE_NOT_FOUND,
                                new String[] { form.getCourseCd() }, null);
                    }
                }

                /*Credit update: if there was none before and user added credit value OR they changed existing...*/
                if ((planItemInfo.getCredit() == null && form.getCredit() != null && hasText(form.getCredit()))
                        || (planItemInfo.getCredit() != null
                                && !planItemInfo.getCredit().toString().equals(form.getCredit()))) {
                    creditUpdated = true;
                    if (form.getCredit() != null && hasText(form.getCredit())) {
                        planItemInfo.setCredit(new BigDecimal(form.getCredit()));
                    } else { // placeholder changed from having credit to unspecified
                        planItemInfo.setCredit(null);
                    }
                }

                /*Note update*/
                RichTextInfo richTextInfo = new RichTextInfo();
                String note = hasText(form.getNote()) ? form.getNote().trim() : null;
                richTextInfo.setFormatted(note);
                richTextInfo.setPlain(note);
                planItemInfo.setDescr(richTextInfo);

                String[] params = {};
                planItemInfo = getAcademicPlanService().updatePlanItem(planItemId, planItemInfo,
                        PlanConstants.CONTEXT_INFO);

                //  Create events
                Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();
                events.putAll(makeAddUpdateEvent(planItemInfo, courseSummaryDetails, form, true));

                if (courseSummaryDetails != null && hasText(courseSummaryDetails.getCode())) {
                    addStatusAlertEvents(courseSummaryDetails.getCode(), atpId, events, true);
                }

                if (creditUpdated
                        && PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED.equals(planItemInfo.getTypeKey())) {
                    events.putAll(makeUpdateTotalCreditsEvent(atpId,
                            PlanConstants.JS_EVENT_NAME.UPDATE_NEW_TERM_TOTAL_CREDITS));
                }
                form.setRequestStatus(PlanForm.REQUEST_STATUS.SUCCESS);
                form.setJavascriptEvents(events);

                return doPlanActionSuccess(form, PlanConstants.SUCCESS_KEY_UPDATED_ITEM, params);

            }

        } catch (DoesNotExistException e) {
            return doPageRefreshError(form, "PlanItem with Id:" + planItemId + " doesnot exist", e);
        } catch (Exception e) {
            return doErrorPage(form, "QuickAdd request to update failed", PlanConstants.UPDATE_FAILED,
                    new String[] { form.getCourseCd() != null ? form.getCourseCd()
                            : EnumerationHelper.getEnumAbbrValForCodeByType(form.getGeneralPlaceholder(),
                                    PlanConstants.PLACE_HOLDER_ENUM_KEY) },
                    e);
        }

        return doErrorPage(form, "QuickAdd request to update failed", PlanConstants.UPDATE_FAILED,
                new String[] { form.getCourseCd() != null ? form.getCourseCd()
                        : EnumerationHelper.getEnumAbbrValForCodeByType(form.getGeneralPlaceholder(),
                                PlanConstants.PLACE_HOLDER_ENUM_KEY) },
                null);
    }

    /**
     * Adds the status alerts to the provided event if any planned activities are available
     *
     * @param courseCode
     * @param atpId
     */

    private void addStatusAlertEvents(String courseCode, String atpId,
            Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events, boolean update) {
        //Add additional add events for Activity data if present
        List<ActivityOfferingItem> plannedActivities = getPlannedActivitiesByCourseAndTerm(courseCode, atpId);
        PlanConstants.JS_EVENT_NAME eventType = update ? PlanConstants.JS_EVENT_NAME.PLAN_ITEM_UPDATED
                : PlanConstants.JS_EVENT_NAME.PLAN_ITEM_ADDED;
        if (!CollectionUtils.isEmpty(plannedActivities)) {

            List<String> plannedActivityCodes = new ArrayList<String>();
            List<String> suspendedActivityCodes = new ArrayList<String>();
            List<String> withdrawnActivityCodes = new ArrayList<String>();

            for (ActivityOfferingItem activityOfferingItem : plannedActivities) {

                if (PlanConstants.SUSPENDED_STATE.equalsIgnoreCase(activityOfferingItem.getStateKey())) {

                    suspendedActivityCodes.add(activityOfferingItem.getCode());

                } else if (PlanConstants.WITHDRAWN_STATE.equalsIgnoreCase(activityOfferingItem.getStateKey())) {

                    withdrawnActivityCodes.add(activityOfferingItem.getCode());

                }

                plannedActivityCodes.add(activityOfferingItem.getCode());

            }

            Collections.sort(plannedActivityCodes);
            String sections = StringUtils.join(plannedActivityCodes.toArray(), ", ");

            if (sections != null) {
                events.get(eventType).put("sections", sections);
            }

            if (!withdrawnActivityCodes.isEmpty() || !suspendedActivityCodes.isEmpty()) {

                events.get(eventType).put("showAlert", "true");

                String statusAlert = events.get(eventType).get("statusAlert");

                StringBuffer sb = new StringBuffer();

                sb = sb.append(statusAlert);

                if (!withdrawnActivityCodes.isEmpty()) {
                    sb = sb.append(String.format(PlanConstants.WITHDRAWN_ALERT,
                            StringUtils.join(withdrawnActivityCodes.toArray(), ", ")));
                }

                if (!suspendedActivityCodes.isEmpty()) {
                    sb = sb.append(String.format(PlanConstants.SUSPENDED_ALERT,
                            StringUtils.join(suspendedActivityCodes.toArray(), ", ")));
                }

                events.get(eventType).put("statusAlert", sb.toString());
            }
        }
    }

    /**
     * Returns synchronized learning plan
     *
     * @param studentId
     * @return
     */
    private LearningPlan getSynchronizedLearningPlan(String studentId) {
        LearningPlan plan = null;
        // Synchronized is used to ensure only one learning plan is created for a given student Id
        synchronized (studentId) {
            try {
                //  If something goes wrong with the query then a RuntimeException will be thrown. Otherwise, the method
                //  will return the default plan or null. Having multiple plans will also produce a RuntimeException.
                plan = getPlanHelper().getLearningPlan(studentId);
            } catch (RuntimeException e) {
                return null;
            }

            /*
            *  Create a default learning plan if there isn't one already and skip querying for plan items.
            */
            if (plan == null) {
                try {
                    plan = createDefaultLearningPlan(studentId);
                } catch (Exception e) {
                    return null;
                }
            }
        }
        return plan;
    }

    /**
     * Build an HTML link to a specific ATP in the quarter view.
     *
     * @param atpId
     * @param text
     * @return
     */
    private String makeLinkToAtp(String atpId, String text) {

        return PlanConstants.QUARTER_LINK.replace("{atpId}", atpId).replace("{label}", text);
    }

    /**
     * Check for duplicate plan items by type.
     *
     * @param atpId
     * @param refObjId
     * @param planItemType
     * @return
     */
    private boolean isDuplicate(String atpId, String refObjId, String planItemType, String courseCd) {
        /*
         Make sure no dups exist. The rules are different for wishlist vs planned or backup courses.
        */
        boolean isDuplicate = false;
        if (planItemType.equals(PlanConstants.LEARNING_PLAN_ITEM_TYPE_WISHLIST)) {
            if (getWishlistPlanItem(refObjId, courseCd) != null) {
                isDuplicate = true;
            }
        } else {
            if (getPlanHelper().getPlannedOrBackupPlanItem(refObjId, courseCd, atpId) != null) {
                isDuplicate = true;
            }
        }
        return isDuplicate;
    }

    /**
     * Determines if a plan has capacity in within a particular ATP for adding a new plan item of a specific type.
     *
     * @param plan
     * @param atpId
     * @param typeKey
     * @return True if the item can be added or false if not.
     * @throws RuntimeException if things go wrong.
     */
    private boolean isAtpHasCapacity(LearningPlan plan, String atpId, String typeKey) {
        if (plan == null) {
            throw new RuntimeException("Plan was NULL.");
        }

        if (StringUtils.isEmpty(atpId)) {
            throw new RuntimeException("Course Id was empty.");
        }

        List<PlanItemInfo> planItems = null;
        PlanItem item = null;
        try {
            planItems = getAcademicPlanService().getPlanItemsInPlanByType(plan.getId(), typeKey,
                    PlanConstants.CONTEXT_INFO);
        } catch (Exception e) {
            throw new RuntimeException("Could not retrieve plan items.", e);
        }

        int counter = 0;
        if (planItems == null) {
            throw new RuntimeException("Could not retrieve plan items.");
        } else {
            for (PlanItem p : planItems) {
                if (p.getPlanPeriods().get(0).equals(atpId)
                        && (PlanConstants.COURSE_TYPE.equalsIgnoreCase(p.getRefObjectType())
                                || isPlaceHolderType(p.getRefObjectType()))) {
                    counter++;
                }
            }
        }

        if (typeKey.equals(PlanConstants.LEARNING_PLAN_ITEM_TYPE_BACKUP)) {
            return (counter >= PlanConstants.BACKUP_PLAN_ITEM_CAPACITY) ? false : true;
        } else if (typeKey.equals(PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED)) {
            return (counter >= PlanConstants.PLANNED_PLAN_ITEM_CAPACITY) ? false : true;
        } else if (PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED.equals(typeKey)) {
            return (counter >= PlanConstants.PLANNED_PLAN_ITEM_CAPACITY) ? false : true;
        }

        throw new RuntimeException(String.format("Unknown plan item type [%s].", typeKey));
    }

    /**
     * Adds the wishList Item
     *
     * @param form
     * @param result
     * @param httprequest
     * @param httpresponse
     * @return
     */
    @RequestMapping(params = "methodToCall=addSavedCourse")
    public ModelAndView addSavedCourse(@ModelAttribute("KualiForm") PlanForm form, BindingResult result,
            HttpServletRequest httprequest, HttpServletResponse httpresponse) {
        if (getUserSessionHelper().isAdviser()) {
            return doAdviserAccessError(form, "Adviser Access Denied", null);
        }

        String courseId = form.getCourseId();
        String courseCd = form.getCode();
        if (StringUtils.isEmpty(courseId)) {
            return doOperationFailedError(form, "Course ID was missing.", null);
        }

        if (StringUtils.isEmpty(courseCd)) {
            return doOperationFailedError(form, "Course CD was missing.", null);
        }

        boolean isCrossListedCourse = false;
        CourseInfo courseInfo = getCourseHelper().getCourseInfo(courseId);
        try {
            isCrossListedCourse = getCourseHelper().isCrossListedCourse(courseInfo, courseCd);
        } catch (DoesNotExistException e) {
            return doErrorPage(form, "Course not found", PlanConstants.COURSE_NOT_FOUND, new String[] { courseCd },
                    null);
        }

        String studentId = getUserSessionHelper().getStudentId();
        LearningPlan plan = getSynchronizedLearningPlan(studentId);
        if (plan == null) {
            return doOperationFailedError(form, "Unable to create learning plan.", null);
        }

        // Retrieve courseDetails based on the passed in CourseId and then update courseDetails based on the version independent Id
        CourseSummaryDetails courseDetails = getVersionVerifiedCourseDetails(courseId,
                isCrossListedCourse ? courseCd : null);
        if (courseDetails == null) {
            return doOperationFailedError(form, "Unable to retrieve Course Details.", null);
        }

        PlanItemInfo planItem = null;
        try {
            planItem = addPlanItem(plan, courseDetails.getVersionIndependentId(), PlanConstants.COURSE_TYPE, null,
                    PlanConstants.LEARNING_PLAN_ITEM_TYPE_WISHLIST, null, null,
                    isCrossListedCourse ? courseCd : null);
        } catch (DuplicateEntryException e) {
            return doDuplicateBookmarkItem(form, courseDetails.getCode());
        } catch (Exception e) {
            return doOperationFailedError(form, "Unable to add plan item.", e);
        }

        //  Create events
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();
        events.putAll(makeAddUpdateEvent(planItem, courseDetails, form, false));

        form.setRequestStatus(PlanForm.REQUEST_STATUS.SUCCESS);
        form.setJavascriptEvents(events);

        return doPlanActionSuccess(form, PlanConstants.SUCCESS_KEY_SAVED_ITEM_ADDED, new String[0]);
    }

    @RequestMapping(params = "methodToCall=removeItem")
    public ModelAndView removePlanItem(@ModelAttribute("KualiForm") PlanForm form, BindingResult result,
            HttpServletRequest httprequest, HttpServletResponse httpresponse) {
        if (getUserSessionHelper().isAdviser()) {
            return doAdviserAccessError(form, "Adviser Access Denied", null);
        }

        return removeItem(form);
    }

    /**
     * Method used to remove planItems
     * Removed course, course placeHolder,  general placeHolder, recommended planItems
     *
     * @param form
     * @return
     */
    private ModelAndView removeItem(PlanForm form) {
        String planItemId = form.getPlanItemId();
        String code = null;
        String title = null;
        boolean isRecommendedItem = false;
        String courseId = form.getCourseId();
        if (StringUtils.isEmpty(planItemId) && StringUtils.isEmpty(courseId)) {
            return doOperationFailedError(form, "Plan item id and courseId are missing.", null);
        }

        if (StringUtils.isEmpty(planItemId)) {
            CourseSummaryDetails course = getCourseDetailsInquiryService().retrieveCourseSummaryByIdAndCd(courseId,
                    form.getCode());
            planItemId = getPlanIdFromCourseId(course.getVersionIndependentId(),
                    PlanConstants.LEARNING_PLAN_ITEM_TYPE_WISHLIST);
        }

        String activityCode = null;
        //  See if the plan item exists.
        PlanItemInfo planItem = null;
        try {
            planItem = getAcademicPlanService().getPlanItem(planItemId, PlanConstants.CONTEXT_INFO);
            isRecommendedItem = PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED.equals(planItem.getTypeKey());
            if (isRecommendedItem) {
                if (!getUserSessionHelper().getCurrentUserId().equals(planItem.getMeta().getCreateId())) {
                    return doNonOwnerAccessError(form, "Non Owner Access Denied", null);
                }
            }
        } catch (DoesNotExistException e) {
            return doPageRefreshError(form, String.format("No plan item with id [%s] exists.", planItemId), e);
        } catch (Exception e) {
            return doOperationFailedError(form, "Query for plan item failed.", e);
        }
        String term = null;
        for (String atpId : planItem.getPlanPeriods()) {
            term = AtpHelper.atpIdToTermName(atpId);
            break;
        }
        CourseSummaryDetails courseDetail = null;
        if (PlanConstants.COURSE_TYPE.equals(planItem.getRefObjectType())) {
            courseDetail = getCourseDetailsInquiryService().retrieveCourseSummaryByIdAndCd(
                    planItem.getRefObjectId(), getPlanHelper().getCrossListedCourse(planItem.getAttributes()));
            courseId = courseDetail.getCourseId();
            code = courseDetail.getCode();
        } else if (PlanConstants.SECTION_TYPE.equals(planItem.getRefObjectType())) {
            String activityId = planItem.getRefObjectId();
            ActivityOfferingDisplayInfo activityDisplayInfo = null;
            CourseOfferingInfo courseOfferingInfo = null;
            try {
                activityDisplayInfo = getCourseOfferingService().getActivityOfferingDisplay(activityId,
                        PlanConstants.CONTEXT_INFO);
            } catch (Exception e) {
                logger.error("Could not retrieve ActivityOffering data for" + activityId, e);
            }
            if (activityDisplayInfo != null) {
                /*TODO: move this to Coursehelper to make it institution neutral*/
                String courseOfferingId = null;
                for (AttributeInfo attributeInfo : activityDisplayInfo.getAttributes()) {
                    if ("PrimaryActivityOfferingId".equalsIgnoreCase(attributeInfo.getKey())) {
                        courseOfferingId = attributeInfo.getValue();
                        break;
                    }
                }

                try {
                    courseOfferingInfo = getCourseOfferingService().getCourseOffering(courseOfferingId,
                            CourseSearchConstants.CONTEXT_INFO);
                } catch (Exception e) {
                    logger.error("Could not retrieve CourseOffering data for" + courseOfferingId, e);
                }

            }
            if (courseOfferingInfo != null && activityDisplayInfo != null) {
                courseId = courseOfferingInfo.getCourseId();
                courseDetail = getCourseDetailsInquiryService().retrieveCourseSummaryByIdAndCd(courseId,
                        courseOfferingInfo.getCourseCode());
                activityCode = activityDisplayInfo.getActivityOfferingCode();

            } else {
                return doOperationFailedError(form, "Could not delete plan item", null);
            }
        } else if ((PlanConstants.PLACE_HOLDER_TYPE_GEN_ED.equals(planItem.getRefObjectType())
                || PlanConstants.PLACE_HOLDER_TYPE.equals(planItem.getRefObjectType()))
                && getUserSessionHelper().isAdviser()) {
            code = EnumerationHelper.getEnumAbbrValForCodeByType(planItem.getRefObjectId(),
                    planItem.getRefObjectType());
            title = EnumerationHelper.getEnumValueForCodeByType(planItem.getRefObjectId(),
                    planItem.getRefObjectType());

        } else if (PlanConstants.PLACE_HOLDER_TYPE_COURSE_LEVEL.equals(planItem.getRefObjectType())
                && getUserSessionHelper().isAdviser()) {
            code = planItem.getRefObjectId();
            DeconstructedCourseCode courseCode = getCourseHelper().getCourseDivisionAndNumber(code);
            Map<String, String> subjectAreas = OrgHelper.getTrimmedSubjectAreas();
            String subjectTitle = subjectAreas.get(courseCode.getSubject());
            String subjectLevel = courseCode.getNumber().toUpperCase()
                    .replace(CourseSearchConstants.COURSE_LEVEL_XX, CourseSearchConstants.COURSE_LEVEL_ZERO);
            title = String.format("%s %s level", subjectTitle, subjectLevel);
        }

        Map<String, String> planItemsToRemove = new HashMap<String, String>();
        if (!AcademicPlanServiceConstants.LEARNING_PLAN_ITEM_TYPE_WISHLIST.equals(planItem.getTypeKey())) {
            if (planItem.getRefObjectType().equalsIgnoreCase(PlanConstants.COURSE_TYPE)) {
                planItemsToRemove = getPlannedSectionsBySectionCd(courseDetail.getCode(), planItem, false, null);
            } else if (form.isPrimary()) {
                planItemsToRemove = getPlannedSectionsBySectionCd(courseDetail.getCode(), planItem, true,
                        activityCode);
            }
        }

        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();

        //  Make events ...

        events.putAll(
                makeRemoveEvent(planItem, courseDetail, form, new ArrayList<String>(planItemsToRemove.values())));

        planItemsToRemove.put(planItemId, null);
        try {
            if (planItemsToRemove.size() > 0) {
                for (String planItemIdToRemove : planItemsToRemove.keySet()) {
                    getAcademicPlanService().deletePlanItem(planItemIdToRemove,
                            getUserSessionHelper().makeContextInfoInstance());
                }
            }
            if (isRecommendedItem) {
                sendRecommendationNotification(term, code, form.getNote(), title, true);
            }
        } catch (Exception e) {
            return doOperationFailedError(form, "Could not delete plan item", e);
        }

        if (planItem.getTypeKey().equals(PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED)) {
            events.putAll(makeUpdateTotalCreditsEvent(planItem.getPlanPeriods().get(0),
                    PlanConstants.JS_EVENT_NAME.UPDATE_NEW_TERM_TOTAL_CREDITS));
        }

        form.setJavascriptEvents(events);
        return doPlanActionSuccess(form, PlanConstants.SUCCESS_KEY_ITEM_DELETED, new String[0]);
    }

    @RequestMapping(params = "methodToCall=removeRecommendedItem")
    public ModelAndView removeRecommendedItem(@ModelAttribute("KualiForm") PlanForm form, BindingResult result,
            HttpServletRequest httprequest, HttpServletResponse httpresponse) {
        if (!getUserSessionHelper().isAdviser()) {
            return doStudentAccessError(form, "Student Access Denied", null);
        }
        return removeItem(form);
    }

    /**
     * @param courseCode
     * @param termId
     * @return
     */
    private List<ActivityOfferingItem> getPlannedActivitiesByCourseAndTerm(String courseCode, String termId) {
        List<ActivityOfferingItem> activityOfferingItems = new ArrayList<ActivityOfferingItem>();
        try {
            List<LearningPlanInfo> learningPlanList = getAcademicPlanService().getLearningPlansForStudentByType(
                    getUserSessionHelper().getStudentId(), PlanConstants.LEARNING_PLAN_TYPE_PLAN,
                    CourseSearchConstants.CONTEXT_INFO);
            //This should be looping only once as student has only one learning plan of plan type
            for (LearningPlanInfo learningPlan : learningPlanList) {
                String learningPlanID = learningPlan.getId();
                List<PlanItemInfo> planItemList = getAcademicPlanService().getPlanItemsInPlanByAtp(learningPlanID,
                        termId, PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED, PlanConstants.CONTEXT_INFO);
                for (PlanItemInfo planItem : planItemList) {
                    String luType = planItem.getRefObjectType();
                    //TODO: if condition assumes refObjId for activities contains course codes
                    if (PlanConstants.SECTION_TYPE.equalsIgnoreCase(luType)) {
                        String activityOfferingId = planItem.getRefObjectId();
                        ActivityOfferingDisplayInfo activityDisplayInfo = null;
                        try {
                            activityDisplayInfo = getCourseOfferingService()
                                    .getActivityOfferingDisplay(activityOfferingId, PlanConstants.CONTEXT_INFO);
                        } catch (Exception e) {
                            logger.error("Could not retrieve ActivityOffering data for" + activityOfferingId, e);
                        }
                        if (activityDisplayInfo != null) {
                            /*TODO: move this to Coursehelper to make it institution neutral*/
                            String courseOfferingId = null;
                            for (AttributeInfo attributeInfo : activityDisplayInfo.getAttributes()) {
                                if ("PrimaryActivityOfferingId".equalsIgnoreCase(attributeInfo.getKey())) {
                                    courseOfferingId = attributeInfo.getValue();
                                    break;
                                }
                            }
                            CourseOfferingInfo courseOfferingInfo = null;
                            try {
                                courseOfferingInfo = getCourseOfferingService().getCourseOffering(courseOfferingId,
                                        CourseSearchConstants.CONTEXT_INFO);
                                if (courseCode.equalsIgnoreCase(courseOfferingInfo.getCourseCode())) {
                                    ActivityOfferingItem activityOfferingItem = getCourseDetailsInquiryService()
                                            .getActivityItem(activityDisplayInfo, courseOfferingInfo,
                                                    !AtpHelper.isAtpSetToPlanning(termId), termId,
                                                    planItem.getId());
                                    activityOfferingItems.add(activityOfferingItem);
                                }
                            } catch (Exception e) {
                                logger.error("Could not retrieve CourseOffering data for" + courseOfferingId, e);
                            }

                        }
                    }
                }

            }
        } catch (Exception e) {
            logger.error("Could not retrieve activities for course:" + courseCode + "for term:" + termId, e);
        }
        return activityOfferingItems;
    }

    /**
     * Used to get the Planned sections as a map with plan item id's and section codes using following params
     *
     * @param courseCd
     * @param planItem
     * @param sectionPrimary
     * @param activityCode
     * @return
     */
    private Map<String, String> getPlannedSectionsBySectionCd(String courseCd, PlanItem planItem,
            boolean sectionPrimary, String activityCode) {
        Map<String, String> plannedSections = new HashMap<String, String>();
        List<ActivityOfferingItem> activityOfferingItems = getPlannedActivitiesByCourseAndTerm(courseCd,
                planItem.getPlanPeriods().get(0));
        for (ActivityOfferingItem activityOfferingItem : activityOfferingItems) {
            if (!activityOfferingItem.getPlanItemId().equalsIgnoreCase(planItem.getId())) {
                if (sectionPrimary && activityCode != null
                        && activityOfferingItem.getCode().startsWith(activityCode)
                        && !activityOfferingItem.isPrimary()) {
                    plannedSections.put(activityOfferingItem.getPlanItemId(),
                            activityOfferingItem.getRegistrationCode());
                } else if (!sectionPrimary) {
                    plannedSections.put(activityOfferingItem.getPlanItemId(),
                            activityOfferingItem.getRegistrationCode());
                }
            }
        }

        return plannedSections;
    }

    /**
     * Blow-up response for all plan item actions for the Adviser.
     */

    private ModelAndView doAdviserAccessError(PlanForm form, String errorMessage, Exception e) {
        String[] params = {};
        return doErrorPage(form, errorMessage, PlanConstants.ERROR_KEY_ADVISER_ACCESS, params, e);
    }

    /**
     * Blow-up response for all plan item actions for the Student.
     */

    private ModelAndView doStudentAccessError(PlanForm form, String errorMessage, Exception e) {
        String[] params = {};
        return doErrorPage(form, errorMessage, PlanConstants.ERROR_KEY_STUDENT_ACCESS, params, e);
    }

    /**
     * Blow-up response for all plan item actions for the non Owners of recommendations.
     */

    private ModelAndView doNonOwnerAccessError(PlanForm form, String errorMessage, Exception e) {
        String[] params = {};
        return doErrorPage(form, errorMessage, PlanConstants.ERROR_KEY_NON_OWNER_ACCESS, params, e);
    }

    /**
     * Blow up response of the plan capacity validation fails.
     *
     * @param form
     * @return
     */
    private ModelAndView doPlanCapacityExceededError(PlanForm form, String type) {
        String errorId = PlanConstants.ERROR_KEY_PLANNED_ITEM_CAPACITY_EXCEEDED;
        if (type.equals(PlanConstants.LEARNING_PLAN_ITEM_TYPE_BACKUP)) {
            errorId = PlanConstants.ERROR_KEY_BACKUP_ITEM_CAPACITY_EXCEEDED;
        }
        return doErrorPage(form, errorId, new String[0]);
    }

    /**
     * Blow up response if the user tries to update plan items in past terms.
     *
     * @param form
     * @return
     */
    private ModelAndView doCannotChangeHistoryError(PlanForm form) {
        return doErrorPage(form, PlanConstants.ERROR_KEY_HISTORICAL_ATP, new String[0]);
    }

    /**
     * Blow-up response for all plan item actions.
     */
    private ModelAndView doPageRefreshError(PlanForm form, String errorMessage, Exception e) {
        // <a href="/student/myplan/plan?methodToCall=start&viewId=PlannedCourses-FormView">Reset your academic plan</a>
        // Removed link because html string is being encoded in the view
        String[] params = {};
        if (e != null) {
            logger.error(errorMessage, e);
        } else {
            logger.error(errorMessage);
        }
        return doErrorPage(form, errorMessage, PlanConstants.ERROR_KEY_PAGE_RESET_REQUIRED, params, e);
    }

    /**
     * Blow-up response for all plan item actions.
     */
    private ModelAndView doOperationFailedError(PlanForm form, String errorMessage, Exception e) {
        String[] params = {};
        if (e != null) {
            logger.error(errorMessage, e);
        } else {
            logger.error(errorMessage);
        }
        return doErrorPage(form, errorMessage, PlanConstants.ERROR_KEY_OPERATION_FAILED, params, e);
    }

    /**
     * Logs errors and passes the request on to the error page.
     */
    private ModelAndView doErrorPage(PlanForm form, String errorMessage, String errorKey, String[] params,
            Exception e) {
        if (e != null) {
            logger.error(errorMessage, e);
        } else {
            logger.error(errorMessage);
        }
        return doErrorPage(form, errorKey, params);
    }

    /**
     * Initializes the error page.
     */
    private ModelAndView doErrorPage(PlanForm form, String errorKey, String[] params) {
        form.setRequestStatus(PlanForm.REQUEST_STATUS.ERROR);
        GlobalVariables.getMessageMap().clearErrorMessages();
        GlobalVariables.getMessageMap().putErrorForSectionId(PlanConstants.PLAN_ITEM_RESPONSE_PAGE_ID, errorKey,
                params);
        return getUIFModelAndView(form, PlanConstants.PLAN_ITEM_RESPONSE_PAGE_ID);
    }

    /**
     * Blow-up response for all plan item actions.
     */
    private ModelAndView doDuplicatePlanItem(PlanForm form, String atpId, String code) {
        String[] params = { code, AtpHelper.atpIdToTermName(atpId) };
        return doErrorPage(form, PlanConstants.ERROR_KEY_PLANNED_ITEM_ALREADY_EXISTS, params);
    }

    /**
     * Blow-up response for all recommended item actions.
     */
    private ModelAndView doDuplicateRecommendedItem(PlanForm form, String atpId, String code) {
        String[] params = { code, AtpHelper.atpIdToTermName(atpId) };
        return doErrorPage(form, PlanConstants.ERROR_KEY_RECOMMENDED_ITEM_ALREADY_EXISTS, params);
    }

    /**
     * Blow-up response for all plan item actions.
     */
    private ModelAndView doDuplicateBookmarkItem(PlanForm form, String code) {
        String[] params = { code };
        return doErrorPage(form, PlanConstants.ERROR_KEY_BOOKMARKED_ITEM_ALREADY_EXISTS, params);
    }

    /**
     * Success response for all plan item actions.
     */
    private ModelAndView doPlanActionSuccess(PlanForm form, String key, String[] params) {
        form.setRequestStatus(PlanForm.REQUEST_STATUS.SUCCESS);
        GlobalVariables.getMessageMap().putInfoForSectionId(PlanConstants.PLAN_ITEM_RESPONSE_PAGE_ID, key, params);
        return getUIFModelAndView(form, PlanConstants.PLAN_ITEM_RESPONSE_PAGE_ID);
    }

    /**
     * Retrieve courseDetails based on the passed in CourseId and then update courseDetails based on the version independent Id
     *
     * @param courseId
     * @return
     */
    private CourseSummaryDetails getVersionVerifiedCourseDetails(String courseId, String courseCd) {
        CourseSummaryDetails courseDetails = null;
        try {

            courseDetails = getCourseDetailsInquiryService().retrieveCourseSummaryByIdAndCd(courseId, courseCd);
            /* Switching the courseDetails based on the versionIndependent Id*/
            if (!courseId.equals(courseDetails.getVersionIndependentId())) {
                courseDetails = getCourseDetailsInquiryService()
                        .retrieveCourseSummaryByIdAndCd(courseDetails.getVersionIndependentId(), courseCd);
            }

        } catch (Exception e) {
            logger.error("Unable to load course details for courseId " + courseId);
        }
        return courseDetails;
    }

    // Course ID GUID, atp key id eg "uw.kuali.atp.2001.1"
    @RequestMapping(value = "/plan/enroll")
    public void getCourseSectionStatusAsJson(HttpServletResponse response, HttpServletRequest request) {
        try {
            String courseId = request.getParameter("courseId");
            String courseCd = request.getParameter("courseCd");
            String atpParam = request.getParameter("atpId");

            CourseSummaryDetails courseDetails = getCourseDetailsInquiryService()
                    .retrieveCourseSummaryByIdAndCd(courseId, courseCd);

            List<AtpHelper.YearTerm> atpList = new ArrayList<AtpHelper.YearTerm>();
            if (atpParam == null || "".equals(atpParam.trim())) {

                List<String> list = courseDetails.getScheduledTerms();
                for (String item : list) {
                    AtpHelper.YearTerm yt = AtpHelper.termToYearTerm(item);
                    atpList.add(yt);
                }
            } else {
                AtpHelper.YearTerm yt = AtpHelper.atpToYearTerm(atpParam);
                atpList.add(yt);
            }

            String curric = courseDetails.getSubjectArea();
            String num = courseDetails.getCourseNumber();

            LinkedHashMap<String, LinkedHashMap<String, Object>> payload = new LinkedHashMap<String, LinkedHashMap<String, Object>>();
            for (AtpHelper.YearTerm yt : atpList) {
                getCourseHelper().getAllSectionStatus(payload, yt.toATP(), curric, num);
            }

            String json = mapper.writeValueAsString(payload);
            response.setHeader("content-type", "application/json");
            response.setHeader("Cache-Control", "No-cache");
            response.setHeader("Cache-Control", "No-store");
            response.setHeader("Cache-Control", "max-age=0");
            response.getWriter().println(json);

        } catch (Exception e) {
            throw new RuntimeException("Unable to retrieve Course Details.", e);
        }

    }

    /**
     * Adds a plan item for the given course id and ATPs.
     *
     * @param plan         The learning plan to add the item to.
     * @param refObjId     The id of the course.
     * @param atpId        A list of ATP/term ids if the plan item is a planned course.
     * @param planItemType Saved course or planned course.
     * @return The newly created plan item or the existing plan item where a plan item already exists for the given course.
     * @throws RuntimeException on errors.
     */
    protected PlanItemInfo addPlanItem(LearningPlan plan, String refObjId, String refObjType, String atpId,
            String planItemType, String note, String credit, String crossListedCourse)
            throws DuplicateEntryException {

        if (StringUtils.isEmpty(refObjId)) {
            throw new RuntimeException("Empty Course ID");
        }

        PlanItemInfo newPlanItem = null;

        PlanItemInfo pii = new PlanItemInfo();
        pii.setLearningPlanId(plan.getId());
        pii.setTypeKey(planItemType);
        pii.setRefObjectType(refObjType);
        pii.setRefObjectId(refObjId);

        pii.setStateKey(PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED.equals(planItemType)
                ? PlanConstants.LEARNING_PLAN_ITEM_PROPOSED_STATE_KEY
                : PlanConstants.LEARNING_PLAN_ITEM_ACTIVE_STATE_KEY);

        RichTextInfo rti = new RichTextInfo();
        rti.setFormatted(hasText(note) ? note : "");
        rti.setPlain(hasText(note) ? note : "");
        pii.setDescr(rti);

        if (null != atpId) {
            pii.setPlanPeriods(Arrays.asList(atpId));
        }

        //  Don't allow duplicates.
        if (!isPlaceHolderType(refObjType) && isDuplicate(atpId, refObjId, planItemType, crossListedCourse)) {
            throw new DuplicateEntryException("Duplicate plan item exists.");
        }

        if (isPlaceHolderType(refObjType) && hasText(credit)) {
            pii.setCredit(new BigDecimal(credit));
        }

        if (!StringUtils.isEmpty(crossListedCourse)) {
            List<AttributeInfo> attributeInfos = new ArrayList<AttributeInfo>();
            AttributeInfo attributeInfo = new AttributeInfo(PlanConstants.CROSS_LISTED_COURSE_ATTR_KEY,
                    crossListedCourse);
            attributeInfos.add(attributeInfo);
            pii.setAttributes(attributeInfos);
        }

        try {
            newPlanItem = getAcademicPlanService().createPlanItem(pii,
                    getUserSessionHelper().makeContextInfoInstance());
        } catch (AlreadyExistsException e) {
            logger.error("Could not create plan item.", e);
            throw new DuplicateEntryException("plan Item already exists", e);
        } catch (Exception e) {
            logger.error("Could not create plan item.", e);
            throw new RuntimeException("Could not create plan item.", e);
        }

        return newPlanItem;
    }

    /**
     * Gets a Plan Item of type "wishlist" for a particular course. There should only ever be one.
     *
     * @param courseId The id of the course.
     * @return A PlanItem of type wishlist.
     */
    protected PlanItemInfo getWishlistPlanItem(String courseId, String courseCd) {

        if (StringUtils.isEmpty(courseId)) {
            throw new RuntimeException("Course Id was empty.");
        }

        String studentId = getUserSessionHelper().getStudentId();
        LearningPlan learningPlan = getPlanHelper().getLearningPlan(studentId);
        if (learningPlan == null) {
            throw new RuntimeException(String.format("Could not find the default plan for [%s].", studentId));
        }

        List<PlanItemInfo> planItems = null;
        PlanItemInfo item = null;

        try {
            planItems = getAcademicPlanService().getPlanItemsInPlanByType(learningPlan.getId(),
                    PlanConstants.LEARNING_PLAN_ITEM_TYPE_WISHLIST, PlanConstants.CONTEXT_INFO);
        } catch (Exception e) {
            throw new RuntimeException("Could not retrieve plan items.", e);
        }

        for (PlanItemInfo p : planItems) {
            if (p.getRefObjectId().equals(courseId)) {
                /*Multiple PlanItems with same refObjId can be present which are crossListed courses in same term,
                So filtering them by using the given crossListed courseCd to get the exact planItemInfo*/
                String crossListedCourse = getPlanHelper().getCrossListedCourse(p.getAttributes());
                //Planned parent course returned
                if (StringUtils.isEmpty(courseCd) && StringUtils.isEmpty(crossListedCourse)) {
                    item = p;
                    break;
                }
                //Planned crossListed course returned
                else if (!StringUtils.isEmpty(courseCd)
                        && getCourseHelper().isSimilarCourses(courseCd, crossListedCourse)) {
                    item = p;
                    break;
                }
            }
        }

        //  A null here means that no plan item exists for the given course ID.
        return item;
    }

    /**
     * Create a new learning plan for the given student.
     *
     * @param studentId
     * @return The plan.
     */
    private LearningPlan createDefaultLearningPlan(String studentId)
            throws InvalidParameterException, DataValidationErrorException, MissingParameterException,
            AlreadyExistsException, PermissionDeniedException, OperationFailedException {

        LearningPlanInfo plan = new LearningPlanInfo();
        plan.setTypeKey(PlanConstants.LEARNING_PLAN_TYPE_PLAN);
        RichTextInfo rti = new RichTextInfo();
        rti.setFormatted("");
        rti.setPlain("");
        plan.setShared(true);
        plan.setDescr(rti);
        plan.setStudentId(studentId);
        plan.setStateKey(PlanConstants.LEARNING_PLAN_ACTIVE_STATE_KEY);
        plan.setMeta(new MetaInfo());

        //  Set the user id in the context used in the web service call.
        ContextInfo context = new ContextInfo();
        context.setPrincipalId(getUserSessionHelper().getStudentId());

        return getAcademicPlanService().createLearningPlan(plan, context);
    }

    /**
     * Creates events map for a remove.
     *
     * @param planItem
     * @return
     */
    private Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> makeRemoveEvent(PlanItemInfo planItem,
            CourseSummaryDetails courseDetails, PlanForm planForm, List<String> itemsToUpdate) {
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();
        Map<String, String> params = new HashMap<String, String>();

        params.put("planItemType", formatTypeKey(planItem.getTypeKey()));
        params.put("planItemId", planItem.getId());

        boolean placeHolder = isPlaceHolderType(planItem.getRefObjectType());
        params.put("placeHolder", String.valueOf(placeHolder));

        if (!placeHolder) {
            params.put("courseId", courseDetails.getCourseId());
            params.put("subject", courseDetails.getSubjectArea().trim());
            params.put("number", courseDetails.getCourseNumber().trim());
            params.put("courseCd", courseDetails.getCode());
        }

        //  Only planned or backup items get an atpId attribute.
        if (PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED.equals(planItem.getTypeKey())
                || PlanConstants.LEARNING_PLAN_ITEM_TYPE_BACKUP.equals(planItem.getTypeKey())
                || PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED.equals(planItem.getTypeKey())) {
            params.put("atpId", planItem.getPlanPeriods().get(0));

            if (PlanConstants.SECTION_TYPE.equals(planItem.getRefObjectType())) {
                String itemsToBeUpdated = null;
                if (itemsToUpdate != null && itemsToUpdate.size() > 0) {
                    itemsToBeUpdated = StringUtils.join(itemsToUpdate.toArray(), ",");
                }

                params.put("SectionCode", planForm.getSectionCode());
                params.put("ActivityOfferingId", planItem.getRefObjectId());
                params.put("RegistrationCode", planForm.getRegistrationCode());
                params.put("InstituteCode", planForm.getInstituteCode());
                params.put("shortTermName", AtpHelper.atpIdToShortTermName(planItem.getPlanPeriods().get(0)));
                params.put("ItemsToUpdate", itemsToBeUpdated);
                params.put("ActivityStateKey", planForm.getActivityStateKey());

                if (courseDetails.getCourseId() != null && planForm.getInstituteCode() != null
                        && planForm.getSectionCode() != null) {
                    String sectionCode = null;
                    List<String> sections = new ArrayList<String>();
                    if (!planForm.isPrimary()) {
                        sectionCode = planForm.getSectionCode().substring(0, 1);
                        sections.add(sectionCode);
                        sections.addAll(
                                getPlannedSectionsBySectionCd(courseDetails.getCode(), planItem, true, sectionCode)
                                        .values());
                        Collections.sort(sections);
                        params.put("PrimaryDeleteHoverText", StringUtils.join(sections, ", "));
                    }

                }
            }

        }

        if (PlanConstants.SECTION_TYPE.equals(planItem.getRefObjectType())) {
            events.put(PlanConstants.JS_EVENT_NAME.SECTION_ITEM_DELETED, params);
        } else {
            events.put(PlanConstants.JS_EVENT_NAME.PLAN_ITEM_DELETED, params);
        }

        return events;
    }

    /**
     * Creates an update credits event.
     *
     * @param atpId The id of the term.
     * @return
     */
    private Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> makeUpdateTotalCreditsEvent(String atpId,
            PlanConstants.JS_EVENT_NAME eventName) {
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();

        Map<String, String> params = new HashMap<String, String>();

        params.put("atpId", atpId);
        String totalCredits = getPlannedTermsHelper().getTotalCredits(atpId);
        params.put("totalCredits", totalCredits);

        events.put(eventName, params);
        return events;
    }

    /**
     * Creates an add or update plan item event.
     *
     * @param planItem
     * @param courseDetails
     * @return
     * @throws RuntimeException if anything goes wrong.
     */
    private Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> makeAddUpdateEvent(PlanItemInfo planItem,
            CourseSummaryDetails courseDetails, PlanForm planForm, boolean updateEvent) {
        Map<PlanConstants.JS_EVENT_NAME, Map<String, String>> events = new LinkedHashMap<PlanConstants.JS_EVENT_NAME, Map<String, String>>();
        Map<String, String> params = new HashMap<String, String>();
        params.put("planItemId", planItem.getId());
        params.put("learningPlanId", planItem.getLearningPlanId());
        params.put("planItemType", formatTypeKey(planItem.getTypeKey()));

        boolean placeHolder = isPlaceHolderType(planItem.getRefObjectType());

        //  Only planned or backup items get an atpId attribute.
        if (PlanConstants.LEARNING_PLAN_ITEM_TYPE_PLANNED.equals(planItem.getTypeKey())
                || PlanConstants.LEARNING_PLAN_ITEM_TYPE_BACKUP.equals(planItem.getTypeKey())
                || PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED.equals(planItem.getTypeKey())) {

            String atpId = planItem.getPlanPeriods().get(0);
            String termName = AtpHelper.atpIdToTermName(atpId);

            params.put("atpId", atpId);
            params.put("termName", termName);

            boolean showAlert = false;
            StringBuffer statusAlert = new StringBuffer();
            if (!placeHolder) {
                // event for alert Icon
                List<String> publishedTerms = AtpHelper.getPublishedTermsForCampus(courseDetails.getCampusCd());
                boolean scheduled = AtpHelper.isCourseOfferedInTerm(atpId, courseDetails.getCode());
                boolean timeScheduleOpen = publishedTerms.contains(atpId);

                if (timeScheduleOpen) {
                    showAlert = !scheduled;
                }

                if (timeScheduleOpen && !scheduled) {
                    statusAlert = statusAlert.append(String.format(PlanConstants.COURSE_NOT_SCHEDULE_ALERT,
                            courseDetails.getCode(), termName));
                }

            }

            if (getUserSessionHelper().isAdviser()) {
                params.put("adviserName", getUserSessionHelper().getName(planItem.getMeta().getCreateId()));
            }

            params.put("showAlert", String.valueOf(showAlert));
            params.put("statusAlert", statusAlert.toString());
            boolean adviserRecommended = false;
            if (!PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED.equals(planItem.getTypeKey())) {
                PlanItemInfo recommendedPlanItem = getPlanHelper().getPlanItemByAtpAndType(
                        planItem.getLearningPlanId(), planItem.getRefObjectId(), atpId,
                        PlanConstants.LEARNING_PLAN_ITEM_TYPE_RECOMMENDED,
                        getPlanHelper().getCrossListedCourse(planItem.getAttributes()));
                adviserRecommended = recommendedPlanItem != null
                        && PlanConstants.LEARNING_PLAN_ITEM_ACCEPTED_STATE_KEY
                                .equals(recommendedPlanItem.getStateKey());
            }

            params.put("adviserRecommended", String.valueOf(adviserRecommended));

            if (PlanConstants.SECTION_TYPE.equals(planItem.getRefObjectType())) {
                params.put("SectionCode", planForm.getSectionCode());
                params.put("ActivityOfferingId", planItem.getRefObjectId());
                params.put("RegistrationCode", planForm.getRegistrationCode());
                params.put("PrimarySectionCode", planForm.getPrimarySectionCode());
                params.put("InstituteCode", planForm.getInstituteCode());
                params.put("Primary", String.valueOf(planForm.isPrimary()));
                params.put("PrimaryPlanItemId", planForm.getPrimaryPlanItemId());
                params.put("ItemsToUpdate", planForm.getPrimaryRegistrationCode());
                if (planForm.getCourseId() != null && planForm.getInstituteCode() != null
                        && planForm.getSectionCode() != null) {
                    String sectionCode = null;
                    List<String> sections = new ArrayList<String>();
                    if (planForm.isPrimary()) {
                        sectionCode = planForm.getSectionCode();
                        sections.add(sectionCode);
                    } else {
                        sectionCode = planForm.getSectionCode().substring(0, 1);
                        sections.add(sectionCode);
                        sections.add(planForm.getSectionCode());
                    }
                    sections.addAll(
                            getPlannedSectionsBySectionCd(courseDetails.getCode(), planItem, true, sectionCode)
                                    .values());
                    Collections.sort(sections);
                    params.put("PrimaryDeleteHoverText", StringUtils.join(sections, ", "));
                }
            }
        }

        String planItemShortTitle = null;
        String planItemLongTitle = null;
        String courseId = null;
        String courseCd = null;
        String subject = null;
        String number = null;
        String credit = null;
        String note = null;
        if (hasText(planItem.getDescr().getPlain())) {
            try {
                note = mapper
                        .writeValueAsString(
                                HtmlUtils.htmlEscape(planItem.getDescr().getPlain().replace("'", "\\'")))
                        .replaceAll("^\"|\"$", "");
            } catch (IOException e) {
                logger.error("Could not add the note to add event");
            }
        }
        if (placeHolder) {
            if (PlanConstants.PLACE_HOLDER_TYPE_COURSE_LEVEL.equals(planItem.getRefObjectType())) {
                planItemShortTitle = planItem.getRefObjectId();
                planItemLongTitle = getCoursePlaceHolderTitle(planItem.getRefObjectId());
                if (planItem.getCredit() != null) {
                    credit = String.valueOf(planItem.getCredit().intValue());
                }
            } else {
                planItemShortTitle = EnumerationHelper.getEnumAbbrValForCodeByType(planItem.getRefObjectId(),
                        planItem.getRefObjectType());
                planItemLongTitle = EnumerationHelper.getEnumValueForCodeByType(planItem.getRefObjectId(),
                        planItem.getRefObjectType());
                if (planItem.getCredit() != null) {
                    credit = String.valueOf(planItem.getCredit().intValue());
                }
            }
        } else {
            planItemShortTitle = courseDetails.getCode();
            planItemLongTitle = courseDetails.getCourseTitle();
            courseId = courseDetails.getCourseId();
            credit = courseDetails.getCredit();
            subject = courseDetails.getSubjectArea().trim();
            number = courseDetails.getCourseNumber().trim();
            courseCd = courseDetails.getCode();
        }

        params.put("placeHolder", String.valueOf(placeHolder));
        params.put("planItemShortTitle", planItemShortTitle);
        params.put("planItemLongTitle", planItemLongTitle);
        params.put("courseId", courseId);
        params.put("courseCd", courseCd);
        params.put("subject", subject);
        params.put("number", number);
        params.put("credit", credit);
        params.put("note", note);

        if (PlanConstants.SECTION_TYPE.equals(planItem.getRefObjectType())) {
            events.put(PlanConstants.JS_EVENT_NAME.SECTION_ITEM_ADDED, params);
        } else {
            if (!updateEvent) {
                events.put(PlanConstants.JS_EVENT_NAME.PLAN_ITEM_ADDED, params);
            } else {
                events.put(PlanConstants.JS_EVENT_NAME.PLAN_ITEM_UPDATED, params);
            }
        }

        return events;
    }

    /**
     * returns for COM 2xx ---> Communication 200 level
     *
     * @param coursePlaceHolder
     * @return
     */
    private String getCoursePlaceHolderTitle(String coursePlaceHolder) {
        Map<String, String> subjectAreas = OrgHelper.getTrimmedSubjectAreas();
        DeconstructedCourseCode courseCode = getCourseHelper().getCourseDivisionAndNumber(coursePlaceHolder);
        String subjectTitle = subjectAreas.get(courseCode.getSubject());
        String subjectLevel = courseCode.getNumber().toUpperCase().replace(CourseSearchConstants.COURSE_LEVEL_XX,
                CourseSearchConstants.COURSE_LEVEL_ZERO);
        return String.format("%s %s level", subjectTitle, subjectLevel);
    }

    private String formatTypeKey(String typeKey) {
        return typeKey.substring(typeKey.lastIndexOf(".") + 1);
    }

    private String getPlanIdFromCourseId(String courseId, String planItemType) {
        String planItemId = null;
        Person user = GlobalVariables.getUserSession().getPerson();
        String studentID = user.getPrincipalId();

        String planTypeKey = PlanConstants.LEARNING_PLAN_TYPE_PLAN;
        List<LearningPlanInfo> learningPlanList = null;

        try {
            learningPlanList = getAcademicPlanService().getLearningPlansForStudentByType(studentID, planTypeKey,
                    CourseSearchConstants.CONTEXT_INFO);
            for (LearningPlanInfo learningPlan : learningPlanList) {
                String learningPlanID = learningPlan.getId();

                List<PlanItemInfo> planItemList = getAcademicPlanService().getPlanItemsInPlanByType(learningPlanID,
                        planItemType, PlanConstants.CONTEXT_INFO);

                for (PlanItemInfo planItem : planItemList) {
                    if (planItem.getRefObjectId().equalsIgnoreCase(courseId)) {
                        planItemId = planItem.getId();
                        break;
                    }
                }
            }
        } catch (Exception e) {
            logger.error("could not get the planItemId");
        }
        return planItemId;
    }

    /**
     * User who has no Learning Plan or auditInfo is considered a new User.
     *
     * @return
     */
    private boolean isNewUser() {
        boolean isNewUser = false;
        try {
            String regId = getUserSessionHelper().getStudentId();
            List<LearningPlanInfo> learningPlanList = getAcademicPlanService().getLearningPlansForStudentByType(
                    regId, PlanConstants.LEARNING_PLAN_TYPE_PLAN, CourseSearchConstants.CONTEXT_INFO);
            /*check if any audits are ran ! if no plans found*/
            if (learningPlanList.isEmpty()) {
                if (regId != null) {
                    Date startDate = new Date();
                    Date endDate = new Date();
                    List<AuditReportInfo> auditReportInfoList = getDegreeAuditService()
                            .getAuditsForStudentInDateRange(regId, startDate, endDate,
                                    DegreeAuditServiceConstants.DEGREE_AUDIT_SERVICE_CONTEXT);
                    isNewUser = auditReportInfoList.isEmpty();
                }
            }
        } catch (Exception e) {
            logger.error("Could not retrieve info", e);
        }
        return isNewUser;
    }

    public DegreeAuditService getDegreeAuditService() {
        if (degreeAuditService == null) {
            degreeAuditService = (DegreeAuditService) GlobalResourceLoader.getService(
                    new QName(DegreeAuditServiceConstants.NAMESPACE, DegreeAuditServiceConstants.SERVICE_NAME));
        }
        return degreeAuditService;
    }

    public synchronized AtpHelper getAtpHelper() {
        if (this.atpHelper == null) {
            this.atpHelper = new AtpHelper();
        }
        return atpHelper;
    }

    public void setAtpHelper(AtpHelper atpHelper) {
        this.atpHelper = atpHelper;
    }

    public AcademicPlanService getAcademicPlanService() {
        if (academicPlanService == null) {
            academicPlanService = (AcademicPlanService) GlobalResourceLoader
                    .getService(new QName(PlanConstants.NAMESPACE, PlanConstants.SERVICE_NAME));
        }
        return academicPlanService;
    }

    public void setAcademicPlanService(AcademicPlanService academicPlanService) {
        this.academicPlanService = academicPlanService;
    }

    public synchronized CourseDetailsInquiryHelperImpl getCourseDetailsInquiryService() {
        if (courseDetailsInquiryService == null) {
            courseDetailsInquiryService = new CourseDetailsInquiryHelperImpl();
        }
        return courseDetailsInquiryService;
    }

    public void setCourseDetailsInquiryService(CourseDetailsInquiryHelperImpl courseDetailsInquiryService) {
        this.courseDetailsInquiryService = courseDetailsInquiryService;
    }

    protected CourseOfferingService getCourseOfferingService() {
        if (this.courseOfferingService == null) {
            //   TODO: Use constants for namespace.
            this.courseOfferingService = (CourseOfferingService) GlobalResourceLoader
                    .getService(new QName("http://student.kuali.org/wsdl/courseOffering", "coService"));
        }
        return this.courseOfferingService;
    }

    public void setCourseOfferingService(CourseOfferingService courseOfferingService) {
        this.courseOfferingService = courseOfferingService;
    }

    public CommentHelper getCommentHelper() {
        if (commentHelper == null) {
            commentHelper = UwMyplanServiceLocator.getInstance().getCommentHelper();
        }
        return commentHelper;
    }

    public void setCommentHelper(CommentHelper commentHelper) {
        this.commentHelper = commentHelper;
    }

    public AcademicRecordService getAcademicRecordService() {
        if (this.academicRecordService == null) {
            //   TODO: Use constants for namespace.
            this.academicRecordService = (AcademicRecordService) GlobalResourceLoader
                    .getService(new QName("http://student.kuali.org/wsdl/academicrecord", "arService"));
        }
        return this.academicRecordService;
    }

    public void setAcademicRecordService(AcademicRecordService academicRecordService) {
        this.academicRecordService = academicRecordService;
    }

    public CourseHelper getCourseHelper() {
        if (courseHelper == null) {
            courseHelper = KsapFrameworkServiceLocator.getCourseHelper();
        }
        return courseHelper;
    }

    public void setCourseHelper(CourseHelper courseHelper) {
        this.courseHelper = courseHelper;
    }

    public PlannedTermsHelperBase getPlannedTermsHelper() {
        if (plannedTermsHelper == null) {
            plannedTermsHelper = new PlannedTermsHelperBase();
        }
        return plannedTermsHelper;
    }

    public void setPlannedTermsHelper(PlannedTermsHelperBase plannedTermsHelper) {
        this.plannedTermsHelper = plannedTermsHelper;
    }

    public PlanHelper getPlanHelper() {
        if (planHelper == null) {
            planHelper = UwMyplanServiceLocator.getInstance().getPlanHelper();
        }
        return planHelper;
    }

    public void setPlanHelper(PlanHelper planHelper) {
        this.planHelper = planHelper;
    }

    public UserSessionHelper getUserSessionHelper() {
        if (userSessionHelper == null) {
            userSessionHelper = UwMyplanServiceLocator.getInstance().getUserSessionHelper();
        }
        return userSessionHelper;
    }

    public void setUserSessionHelper(UserSessionHelper userSessionHelper) {
        this.userSessionHelper = userSessionHelper;
    }

    @Override
    @RequestMapping(method = RequestMethod.GET, params = "methodToCall=performFieldSuggest")
    public @ResponseBody AttributeQueryResult performFieldSuggest(@ModelAttribute("KualiForm") UifFormBase form,
            BindingResult result, HttpServletRequest request, HttpServletResponse response) {

        // invoke attribute query service to perform the query
        AttributeQueryResult queryResult = new AttributeQueryResult();

        Object[] placeHolders = new Object[] { "Capstone", "Composition", "Elective", "Foreign Language",
                "I&S (Individuals and Societies)", "Independent Study", "Internship", "Job", "NW (Natural World)",
                "Other", "Practicum", "Project", "Q/SR (Quantitative and Symbolic Reasoning)", "Research",
                "Seminar", "Study Abroad", "Thesis", "VLPA (Visual, Literary, and Performing Arts)",
                "W (Writing)" };

        queryResult.setResultData(Arrays.asList(placeHolders));

        return queryResult;
    }
}