org.sakaiproject.signup.tool.jsf.SignupUIBaseBean.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.signup.tool.jsf.SignupUIBaseBean.java

Source

/*
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* The Apereo Foundation licenses this file to you under the Educational 
* Community License, Version 2.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://opensource.org/licenses/ecl2.txt
* 
* 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.sakaiproject.signup.tool.jsf;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;

import javax.faces.context.FacesContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.Getter;
import lombok.Setter;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.LogFactoryImpl;
import org.sakaiproject.signup.logic.SakaiFacade;
import org.sakaiproject.signup.logic.SignupCalendarHelper;
import org.sakaiproject.signup.logic.SignupMeetingService;
import org.sakaiproject.signup.logic.SignupMessageTypes;
import org.sakaiproject.signup.logic.SignupUserActionException;
import org.sakaiproject.signup.model.MeetingTypes;
import org.sakaiproject.signup.model.SignupAttachment;
import org.sakaiproject.signup.model.SignupAttendee;
import org.sakaiproject.signup.model.SignupMeeting;
import org.sakaiproject.signup.model.SignupTimeslot;
import org.sakaiproject.signup.tool.jsf.attachment.AttachmentHandler;
import org.sakaiproject.signup.tool.util.SignupBeanConstants;
import org.sakaiproject.signup.tool.util.Utilities;
import org.sakaiproject.time.api.Time;
import org.sakaiproject.time.api.TimeService;
import org.sakaiproject.user.api.User;

/**
 * <p>
 * This is a abstract base class for JSF Signup tool UIBean. It provides some
 * must-have or common used methods such as getMeetingWrapper(), sakakFacade
 * etc.
 * </P>
 */
abstract public class SignupUIBaseBean implements SignupBeanConstants, SignupMessageTypes, MeetingTypes {

    protected SakaiFacade sakaiFacade;

    protected SignupMeetingService signupMeetingService;

    @Getter
    @Setter
    protected SignupCalendarHelper calendarHelper;

    private AttachmentHandler attachmentHandler;

    protected SignupMeetingWrapper meetingWrapper;

    protected List<TimeslotWrapper> timeslotWrappers;

    protected TimeslotWrapper timeslotWrapper;

    protected boolean currentUserSignedup;

    protected static boolean DEFAULT_SEND_EMAIL = "true".equalsIgnoreCase(
            Utilities.getSignupConfigParamVal("signup.default.email.notification", "true")) ? true : false;

    protected static boolean DEFAULT_EXPORT_TO_CALENDAR_TOOL = "true".equalsIgnoreCase(
            Utilities.getSignupConfigParamVal("signup.default.export.to.calendar.setting", "true")) ? true : false;

    protected boolean publishToCalendar = DEFAULT_EXPORT_TO_CALENDAR_TOOL;

    protected boolean sendEmail = DEFAULT_SEND_EMAIL;

    protected Log logger = LogFactoryImpl.getLog(getClass());

    protected Boolean publishedSite;

    //protected boolean sendEmailAttendeeOnly = false;

    protected String sendEmailToSelectedPeopleOnly = SEND_EMAIL_ALL_PARTICIPANTS;

    private int maxSlots;

    private int maxAttendeesPerSlot;

    protected String customLocation;

    protected String customCategory;

    protected static final String ICS_MIME_TYPE = "text/calendar";

    /**
     * This method will get the most updated event/meeting data and handle all
     * the wrapping process for UI. Due to effeciency, the data is only
     * guarantied fresh at a 10 minutes interval (defined by
     * <b>dataRefreshInterval</b> value).
     * 
     * @return a SignupMeetingWrapper object.
     */
    public SignupMeetingWrapper getMeetingWrapper() {
        if (meetingWrapper != null && meetingWrapper.isRefresh()) {
            try {
                SignupMeeting meeting = signupMeetingService.loadSignupMeeting(meetingWrapper.getMeeting().getId(),
                        sakaiFacade.getCurrentUserId(), sakaiFacade.getCurrentLocationId());
                meetingWrapper.setMeeting(meeting);
                updateTimeSlotWrappers(meetingWrapper);
            } catch (Exception e) {
                Utilities.addErrorMessage(Utilities.rb.getString("db.error_or_event.notExisted"));
                logger.error(Utilities.rb.getString("db.error_or_event.notExisted") + " - " + e.getMessage());
            }
        }
        return meetingWrapper;
    }

    /** process new data into Timeslot wrapper for UI purpose */
    protected void updateTimeSlotWrappers(SignupMeetingWrapper meetingWrapper) {
        SignupMeeting meeting = this.meetingWrapper.getMeeting();
        if (meeting == null)
            return;

        List timeslots = meeting.getSignupTimeSlots();
        if (timeslots == null)
            return;

        List<TimeslotWrapper> timeslotWrapperList = new ArrayList<TimeslotWrapper>();
        setCurrentUserSignedup(false);// reset and make sure to capture new
        // changes
        int i = 0;
        int totalSignedupSlots = 0;
        for (Iterator iter = timeslots.iterator(); iter.hasNext();) {
            SignupTimeslot elm = (SignupTimeslot) iter.next();
            TimeslotWrapper tsw = new TimeslotWrapper(elm, sakaiFacade.getCurrentUserId());

            List<AttendeeWrapper> attendeeWrp = new ArrayList<AttendeeWrapper>();
            int posIndex = 0;
            //clean the list
            List<SignupAttendee> cleanedList = getValidAttendees(elm.getAttendees());
            for (SignupAttendee attendee : cleanedList) {
                AttendeeWrapper attWrp = new AttendeeWrapper(attendee,
                        sakaiFacade.getUserDisplayLastFirstName(attendee.getAttendeeUserId()));
                attWrp.setPositionIndex(posIndex++);
                attendeeWrp.add(attWrp);

                /* current user is already signed up in one of the timeslot */
                if (attendee.getAttendeeUserId().equals(sakaiFacade.getCurrentUserId()))
                    //setCurrentUserSignedup(true);
                    totalSignedupSlots++;
            }

            //sorting by displayname
            //JIRA: Signup-204
            posIndex = 0;
            Collections.sort(attendeeWrp);
            for (AttendeeWrapper attWrp : attendeeWrp) {
                attWrp.setPositionIndex(posIndex++);
            }

            tsw.setAttendeeWrappers(attendeeWrp);

            tsw.setWaitingList(wrapWaiters(elm.getWaitingList()));
            tsw.setPositionInTSlist(i++);
            timeslotWrapperList.add(tsw);

        }
        int preferredSlot = meeting.getMaxNumOfSlots();
        if (totalSignedupSlots >= preferredSlot) {
            setCurrentUserSignedup(true);
        }
        setTimeslotWrappers(timeslotWrapperList);

    }

    /** process the new data into Meeting wrapper for UI purpose */
    protected String updateMeetingwrapper(SignupMeeting meeting, String destinationUrl) {
        /* if null,reload due to exception */
        try {
            if (meeting == null)
                meeting = signupMeetingService.loadSignupMeeting(this.meetingWrapper.getMeeting().getId(),
                        sakaiFacade.getCurrentUserId(), sakaiFacade.getCurrentLocationId());

            getMeetingWrapper().setMeeting(meeting);
            getMeetingWrapper().resetAvailableStatus();// re-process avail.
            // status
            updateTimeSlotWrappers(getMeetingWrapper());
            return destinationUrl;
        } catch (Exception e) {
            Utilities.addErrorMessage(Utilities.rb.getString("db.error_or_event.notExisted"));
            logger.warn(Utilities.rb.getString("db.error_or_event.notExisted") + " - " + e.getMessage());
            Utilities.resetMeetingList();
            return MAIN_EVENTS_LIST_PAGE_URL;
        }
    }

    protected void updateSignupAttachmentWrapper(SignupMeeting meeting) {

    }

    /**
     * setup the event/meeting's signup begin and deadline time and validate it
     * too
     */
    protected void setSignupBeginDeadlineData(SignupMeeting meeting, int signupBegin, String signupBeginType,
            int signupDeadline, String signupDeadlineType) throws Exception {
        Date sBegin = Utilities.subTractTimeToDate(meeting.getStartTime(), signupBegin, signupBeginType);
        Date sDeadline = Utilities.subTractTimeToDate(meeting.getEndTime(), signupDeadline, signupDeadlineType);

        if (!START_NOW.equals(signupBeginType) && sBegin.before(new Date())) {
            // a warning for user
            Utilities.addErrorMessage(
                    Utilities.rb.getString("warning.your.event.singup.begin.time.passed.today.time"));
        }

        meeting.setSignupBegins(sBegin);

        if (sBegin.after(sDeadline))
            throw new SignupUserActionException(Utilities.rb.getString("signup.deadline.is.before.signup.begin"));

        meeting.setSignupDeadline(sDeadline);
    }

    public boolean isMeetingOverRepeatPeriod(Date startTime, Date endTime, int repeatPeriodInDays) {
        long duration = endTime.getTime() - startTime.getTime();
        if (24 * repeatPeriodInDays - duration / (MINUTE_IN_MILLISEC * Hour_In_MINUTES) >= 0)
            return false;

        return true;
    }

    /** convert SignupAttendee to AttendeeWrapper object */
    /*private List<AttendeeWrapper> wrapAttendees(List<SignupAttendee> attendees) {
       List<AttendeeWrapper> attendeeWrp = new ArrayList<AttendeeWrapper>();
       int posIndex = 0;
           
       //clean the list
       List<SignupAttendee> cleanedList = getValidAttendees(attendees);
           
       for (SignupAttendee attendee : cleanedList) {
     AttendeeWrapper attWrp = new AttendeeWrapper(attendee, sakaiFacade.getUserDisplayName(attendee.getAttendeeUserId()));
     attWrp.setPositionIndex(posIndex++);
     attendeeWrp.add(attWrp);
        
     // current user is already signed up in one of the timeslot 
     if (attendee.getAttendeeUserId().equals(sakaiFacade.getCurrentUserId())) {
        setCurrentUserSignedup(true);
     }
       }
       return attendeeWrp;
    }*/

    /** convert SignupAttendee to AttendeeWrapper object */
    private List<AttendeeWrapper> wrapWaiters(List<SignupAttendee> attendees) {
        List<AttendeeWrapper> attendeeWrp = new ArrayList<AttendeeWrapper>();
        for (SignupAttendee attendee : attendees) {
            attendeeWrp.add(new AttendeeWrapper(attendee,
                    sakaiFacade.getUserDisplayLastFirstName(attendee.getAttendeeUserId())));
        }

        return attendeeWrp;
    }

    /**
     * This is a setter.
     * 
     * @param meetingWrapper
     *            a SignupMeetingWrapper object.
     */
    public void setMeetingWrapper(SignupMeetingWrapper meetingWrapper) {
        this.meetingWrapper = meetingWrapper;
    }

    /**
     * Get a SakaiFacade object.
     * 
     * @return a SakaiFacade object.
     */
    public SakaiFacade getSakaiFacade() {
        return sakaiFacade;
    }

    /**
     * @return the maxSlots
     */
    public int getMaxSlots() {
        String maxSlotsStringVal = Utilities.getSignupConfigParamVal("signup.maxSlots", "500");
        try {
            maxSlots = Integer.parseInt(maxSlotsStringVal);
        } catch (Exception e) {
            maxSlots = 500;
        }
        return maxSlots;
    }

    /**
     * @param maxSlots the maxSlots to set
     */
    public void setMaxSlots(int maxSlots) {
        this.maxSlots = maxSlots;
    }

    /**
     * @return the maxAttendeesPerSlot
     */
    public int getMaxAttendeesPerSlot() {
        String maxAttendeesStringVal = Utilities.getSignupConfigParamVal("signup.maxAttendeesPerSlot", "500");
        try {
            maxAttendeesPerSlot = Integer.parseInt(maxAttendeesStringVal);
        } catch (Exception e) {
            maxAttendeesPerSlot = 500;
        }
        return maxAttendeesPerSlot;
    }

    /**
     * @param maxAttendeesPerSlot the maxAttendeesPerSlot to set
     */
    public void setMaxAttendeesPerSlot(int maxAttendeesPerSlot) {
        this.maxAttendeesPerSlot = maxAttendeesPerSlot;
    }

    /**
     * This is a setter.
     * 
     * @param sakaiFacade
     *            a SakaiFacade object.
     */
    public void setSakaiFacade(SakaiFacade sakaiFacade) {
        this.sakaiFacade = sakaiFacade;
    }

    /**
     * Get a SignupMeetingService object.
     * 
     * @return a SignupMeetingService object.
     */
    public SignupMeetingService getSignupMeetingService() {
        return signupMeetingService;
    }

    /**
     * This is a setter.
     * 
     * @param signupMeetingService
     *            a SignupMeetingService object.
     */
    public void setSignupMeetingService(SignupMeetingService signupMeetingService) {
        this.signupMeetingService = signupMeetingService;
    }

    /**
     * This is for UI purpose to see if current user has signed up in the
     * event/meeting.
     */
    public boolean isCurrentUserSignedup() {
        return currentUserSignedup;
    }

    /**
     * This is a setter.
     * 
     * @param currentUserSignedup
     *            a boolean value.
     */
    public void setCurrentUserSignedup(boolean currentUserSignedup) {
        this.currentUserSignedup = currentUserSignedup;
    }

    /**
     * Get a list of TimeslotWrapper objects.
     * 
     * @return a list of TimeslotWrapper objects.
     */
    public List<TimeslotWrapper> getTimeslotWrappers() {
        return timeslotWrappers;
    }

    /**
     * This is a setter.
     * 
     * @param timeslotWrappers
     *            a list of TimeslotWrapper objects.
     */
    public void setTimeslotWrappers(List<TimeslotWrapper> timeslotWrappers) {
        this.timeslotWrappers = timeslotWrappers;
    }

    /**
     * This is only for UI purpose to check if the event/meeting is an open
     * session style and signup is not required.
     */
    public boolean getAnnouncementType() {
        boolean anoun = false;
        if (meetingWrapper != null && meetingWrapper.getMeeting() != null
                && ANNOUNCEMENT.equals(meetingWrapper.getMeeting().getMeetingType()))
            anoun = true;

        return anoun;
    }

    /**
     * This is only for UI purpose to check if the event/meeting is an
     * individual style (manay time slots) and it requires signup.
     */
    public boolean getIndividualType() {
        return INDIVIDUAL.equals(meetingWrapper.getMeeting().getMeetingType());
    }

    /**
     * This is only for UI purpose to check if the event/meeting is an group
     * style (only one time slot) and it requires signup.
     */
    public boolean getGroupType() {
        return GROUP.equals(meetingWrapper.getMeeting().getMeetingType());
    }

    /**
     * This is only for UI purpose to check if the event/meeting is an
     * individual style (manay time slots) and it requires signup.
     */
    public boolean getCustomTsType() {
        return CUSTOM_TIMESLOTS.equals(meetingWrapper.getMeeting().getMeetingType());
    }

    /**
     * Get a TimeslotWrapper object for UI.
     * 
     * @return an TimeslotWrapper object.
     */
    public TimeslotWrapper getTimeslotWrapper() {
        return timeslotWrapper;
    }

    /**
     * This is a setter.
     * 
     * @param timeslotWrapper
     *            a TimeslotWrapper object.
     */
    public void setTimeslotWrapper(TimeslotWrapper timeslotWrapper) {
        this.timeslotWrapper = timeslotWrapper;
    }

    /**
     * Check if email should be sent away. This is used by organizer of an
     * event/meeting.
     * 
     * @return true if email should be sent away.
     */
    public boolean isSendEmail() {
        if (!getPublishedSite())
            sendEmail = false;

        return sendEmail;
    }

    /**
     * This is a setter.
     * 
     * @param sendEmail
     *            a boolean value.
     */
    public void setSendEmail(boolean sendEmail) {
        this.sendEmail = sendEmail;
    }

    /**
     * This is a getter method for UI.
     * 
     * @return a constant string.
     */
    public String getIndividual() {
        return INDIVIDUAL;
    }

    /**
     * This is a getter method for UI.
     * 
     * @return a constant string.
     */
    public String getGroup() {
        return GROUP;
    }

    /**
     * This is a getter method for UI.
     * 
     * @return a constant string.
     */
    public String getAnnouncement() {
        return ANNOUNCEMENT;
    }

    /**
     * This is a getter method for UI
     * 
     * @return true if the site is published.
     */
    public Boolean getPublishedSite() {
        if (this.publishedSite == null) {
            try {
                boolean status = sakaiFacade.getSiteService().getSite(sakaiFacade.getCurrentLocationId())
                        .isPublished();
                this.publishedSite = new Boolean(status);

            } catch (Exception e) {
                logger.warn(e.getMessage());
                this.publishedSite = new Boolean(false);

            }
        }

        return publishedSite.booleanValue();
    }

    public void cleanUpUnusedAttachmentCopies(List<SignupAttachment> attachList) {
        if (attachList != null) {
            for (SignupAttachment attach : attachList) {
                getAttachmentHandler().removeAttachmentInContentHost(attach);
            }
            attachList.clear();
        }
    }

    public boolean getSignupAttachmentEmpty() {
        return this.meetingWrapper.getEmptyEventMainAttachment();
    }

    public AttachmentHandler getAttachmentHandler() {
        return attachmentHandler;
    }

    public void setAttachmentHandler(AttachmentHandler attachmentHandler) {
        this.attachmentHandler = attachmentHandler;
    }

    protected void markerTimeslots(List<TimeslotWrapper> TimeSlotWrpList) {
        int i = 0;
        if (TimeSlotWrpList != null) {
            for (TimeslotWrapper tsWrp : TimeSlotWrpList) {
                tsWrp.setTsMarker(i);
                i++;
            }
        }
    }

    public boolean isPublishToCalendar() {
        return publishToCalendar;
    }

    public void setPublishToCalendar(boolean publishToCalendar) {
        this.publishToCalendar = publishToCalendar;
    }

    /*public boolean getSendEmailAttendeeOnly() {
       return sendEmailAttendeeOnly;
    }
        
    public void setSendEmailAttendeeOnly(boolean sendEmailAttendeeOnly) {
       this.sendEmailAttendeeOnly = sendEmailAttendeeOnly;
    }*/

    public String getSendEmailToSelectedPeopleOnly() {
        return sendEmailToSelectedPeopleOnly;
    }

    public void setSendEmailToSelectedPeopleOnly(String sendEmailToSelectedPeopleOnly) {
        this.sendEmailToSelectedPeopleOnly = sendEmailToSelectedPeopleOnly;
    }

    /**
     * Clean the list of attendees by checking that each user is valid
     * @param attendees   List of attendees to be cleaned
     * @return   the cleaned list
     */
    public List<SignupAttendee> getValidAttendees(List<SignupAttendee> attendees) {
        List<SignupAttendee> cleanedList = new ArrayList<SignupAttendee>();

        for (SignupAttendee attendee : attendees) {
            if (sakaiFacade.checkForUser(attendee.getAttendeeUserId())) {
                cleanedList.add(attendee);
            }
        }

        return cleanedList;
    }

    /**
     * Gets the userId for a user, given an eid or an email address. 
     * We check if it matches the eid first, then if it matches an email address.
     * If nothing, return null.
     * 
     * @param value      the string to lookup, could be an eid or an email address
     * @return   the userId or null if User cannot be found
     */
    public String getUserIdForEidOrEmail(String value) {
        User u = sakaiFacade.getUserByEid(value);
        if (u == null) {
            u = sakaiFacade.getUserByEmail(value);
        }

        if (u != null) {
            return u.getId();
        }

        return null;
    }

    /**
     * Get the eids assocaited with an email address, ie there may be two or more users with the same email address. 
     * We need to be able to handle this in the UI.
     * 
     * @param email
     * @return   List<String> of eids.
     */
    public List<String> getEidsForEmail(String email) {
        List<User> users = sakaiFacade.getUsersByEmail(email);

        List<String> eids = new ArrayList<String>();
        for (User u : users) {
            eids.add(u.getEid());
        }

        return eids;
    }

    // Generate a group title based on the input given
    public String generateGroupTitle(String meetingTitle, SignupTimeslot timeslot) {

        final char SEPARATOR = '-';

        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmm");

        StringBuilder sb = new StringBuilder();

        sb.append(meetingTitle);
        sb.append(SEPARATOR);
        sb.append(df.format(timeslot.getStartTime()));
        sb.append(SEPARATOR);
        sb.append(df.format(timeslot.getEndTime()));

        return sb.toString();
    }

    //generate a group description
    public String generateGroupDescription(String meetingTitle, SignupTimeslot timeslot) {
        return Utilities.rb.getString("group.description.default");
    }

    //convert a list of SignupAttendees to a list of userIds
    public List<String> convertAttendeesToUuids(List<SignupAttendee> attendees) {

        List<String> uuids = new ArrayList<String>();

        for (SignupAttendee a : attendees) {
            uuids.add(a.getAttendeeUserId());
        }

        return uuids;
    }

    //convert a list of AttendeeWrappers to a list of userIds
    public List<String> convertAttendeeWrappersToUuids(List<AttendeeWrapper> attendees) {

        List<String> uuids = new ArrayList<String>();

        for (AttendeeWrapper a : attendees) {
            uuids.add(a.getSignupAttendee().getAttendeeUserId());
        }

        return uuids;
    }

    /**
     * Helper to get a formatted string of all attendee email addresses for all tineslots
     * so we can use them in a mailto link
     * @return String of all email addresses
     */
    public String getAllAttendeesEmailAddressesFormatted() {

        Set<String> emails = new HashSet<String>();

        StringBuilder sb = new StringBuilder();
        for (TimeslotWrapper tsWrapper : timeslotWrappers) {
            for (AttendeeWrapper atWrapper : tsWrapper.getAttendeeWrappers()) {
                String email = atWrapper.getAttendeeEmail();
                if (StringUtils.isNotBlank(email)) {
                    emails.add(email);
                }
            }
        }

        for (String e : emails) {
            sb.append(e);
            //for compatibility with Outlook, this should be a semicolon not a comma as per the RFC. 
            //Also tested in Thunderbird, Yahoo and GMail.
            sb.append(';');
        }

        //trim off last , and return
        return StringUtils.removeEnd(sb.toString(), ";");
    }

    /**
     * Generate and send for download an ICS file for the meeting. Contains no timeslots, just the meeting itself.
     * This method is in this particular bean because 1. We have access to the meeting here, and 2. it is used in more than one sub-bean.
     */

    private UserTimeZone userTimeZone;

    public void downloadICSForMeeting() {
        String filePath;
        SignupMeeting meeting = meetingWrapper.getMeeting();

        Date defaultEndTime = meeting.getEndTime();
        Date dfaultStartTime = meeting.getStartTime();

        //pass user preference time in and need to reset back since the object is cached.
        meeting.setEndTime(getUserTimezonePreferenceDate(defaultEndTime));
        meeting.setStartTime(getUserTimezonePreferenceDate(dfaultStartTime));

        try {
            filePath = calendarHelper.createCalendarFile(
                    Collections.singletonList(calendarHelper.generateVEventForMeeting(meeting)));
            meeting.setEndTime(defaultEndTime);
            meeting.setStartTime(dfaultStartTime);
        } catch (NullPointerException ne) {
            meeting.setEndTime(defaultEndTime);
            meeting.setStartTime(dfaultStartTime);
            handleICSDownloadWarningToUser();
            return;
        }

        if (StringUtils.isNotBlank(filePath)) {
            logger.debug("filepath: " + filePath);
            sendDownload(filePath, ICS_MIME_TYPE);
        } else {
            logger.error("Could not generate file for download");
            //TODO this could set an error and return perhaps.
        }
    }

    public void downloadICSForMeetingTimeSlot(TimeslotWrapper timeslotWrapper) {
        String filePath;

        SignupMeeting meeting = meetingWrapper.getMeeting();
        SignupTimeslot currentTimeslot = timeslotWrapper.getTimeSlot();

        Date timeslotEndTime = currentTimeslot.getEndTime();
        Date timeslotStartTime = currentTimeslot.getStartTime();

        Date defaultEndTime = meeting.getEndTime();
        Date dfaultStartTime = meeting.getStartTime();

        //pass user preference time in and need to reset back since the object is cached.
        meeting.setEndTime(getUserTimezonePreferenceDate(defaultEndTime));
        meeting.setStartTime(getUserTimezonePreferenceDate(dfaultStartTime));
        if (currentTimeslot != null) {
            currentTimeslot.setStartTime(getUserTimezonePreferenceDate(timeslotStartTime));
            currentTimeslot.setEndTime(getUserTimezonePreferenceDate(timeslotEndTime));
        }

        try {
            filePath = calendarHelper.createCalendarFile(
                    Collections.singletonList(calendarHelper.generateVEventForTimeslot(meeting, currentTimeslot)));
            //reset timezone back on serverside
            if (currentTimeslot != null) {
                currentTimeslot.setStartTime(timeslotStartTime);
                currentTimeslot.setEndTime(timeslotEndTime);
            }
            meeting.setEndTime(defaultEndTime);
            meeting.setStartTime(dfaultStartTime);
        } catch (NullPointerException ne) {
            if (currentTimeslot != null) {
                currentTimeslot.setStartTime(timeslotStartTime);
                currentTimeslot.setEndTime(timeslotEndTime);
            }
            meeting.setEndTime(defaultEndTime);
            meeting.setStartTime(dfaultStartTime);

            handleICSDownloadWarningToUser();
            return;
        }

        if (StringUtils.isNotBlank(filePath)) {
            logger.debug("filepath: " + filePath);
            sendDownload(filePath, ICS_MIME_TYPE);
        } else {
            logger.error("Could not generate file for download");
            //TODO this could set an error and return perhaps.
        }

    }

    private Date getUserTimezonePreferenceDate(Date dateBasedOnServerTimezone) {
        /*
         * Since the external-calendar-service don't honor the timezone information (in
         * iCal file), which is passed by Date object. Here, we just convert it manually here.
         * Once the external-claendar-service can do the job, this method can be removed.
         */
        TimeService timeService = sakaiFacade.getTimeService();
        TimeZone currentUserTimeZone = timeService.getLocalTimeZone();

        Calendar cal = Calendar.getInstance();
        cal.setTime(dateBasedOnServerTimezone);
        cal.setTimeZone(currentUserTimeZone);

        //get user Pref display hour, day, month and year
        int userPrefMinute = cal.get(cal.MINUTE);
        int userPrefHour = cal.get(cal.HOUR_OF_DAY);
        int userPrefDay = cal.get(cal.DAY_OF_MONTH);
        int userPrefMonth = cal.get(cal.MONTH);
        int userPreYear = cal.get(cal.YEAR);

        Calendar calNew = Calendar.getInstance();
        calNew.setTime(dateBasedOnServerTimezone);
        calNew.set(cal.MINUTE, userPrefMinute);
        calNew.set(cal.HOUR_OF_DAY, userPrefHour);
        calNew.set(cal.DAY_OF_MONTH, userPrefDay);
        calNew.set(cal.MONTH, userPrefMonth);
        calNew.set(cal.YEAR, userPreYear);
        return calNew.getTime();
    }

    private void handleICSDownloadWarningToUser() {
        logger.error("The site calendar could not be retrieved when using the Signup tool");
        String warningFileName = Utilities.rb.getString("ics_file_name_for_failure_warning");
        String warningMsg = Utilities.rb.getString("ics_message_for_failure_warning");
        sendDownloadWarning(warningFileName, warningMsg);
    }

    /**
     * Send a file for download. 
     * 
     * @param filePath
     * 
     */
    protected void sendDownload(String filePath, String mimeType) {

        FacesContext fc = FacesContext.getCurrentInstance();
        ServletOutputStream out = null;
        FileInputStream in = null;

        String filename = StringUtils.substringAfterLast(filePath, File.separator);

        try {
            HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse();

            response.reset();
            response.setHeader("Pragma", "public");
            response.setHeader("Cache-Control", "public, must-revalidate, post-check=0, pre-check=0, max-age=0");
            response.setContentType(mimeType);
            response.setHeader("Content-disposition", "attachment; filename=" + filename);

            in = FileUtils.openInputStream(new File(filePath));
            out = response.getOutputStream();

            IOUtils.copy(in, out);

            out.flush();
        } catch (IOException ex) {
            logger.warn("Error generating file for download:" + ex.getMessage());
        } finally {
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(out);
        }
        fc.responseComplete();

    }

    /**
     * Send a warning message to user about failed ICS file generation
     * @param fileName
     * @param warningMsg
     */
    protected void sendDownloadWarning(String fileName, String warningMsg) {

        FacesContext fc = FacesContext.getCurrentInstance();
        ServletOutputStream out = null;

        try {
            HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse();

            response.reset();
            response.setHeader("Pragma", "public");
            response.setHeader("Cache-Control", "public, must-revalidate, post-check=0, pre-check=0, max-age=0");
            response.setContentType("text/plain");
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);

            out = response.getOutputStream();
            warningMsg = warningMsg != null ? warningMsg : "Missing Scheduler tool on site";
            out.print(warningMsg);

            out.flush();
        } catch (IOException ex) {
            logger.warn("Error generating file for download:" + ex.getMessage());
        } finally {
            try {
                out.close();
            } catch (Exception e) {
                //do nothing;
            }
        }
        fc.responseComplete();

    }

    /**
     * Is ICS calendar generation enabled in the external calendaring service?
     * @return   true/false
     */
    public boolean isIcsEnabled() {
        return calendarHelper.isIcsEnabled();
    }

    private String iframeId = "";

    /**
     * This is a getter method which provide current Iframe id for refresh
     * IFrame purpose.
     * 
     * @return a String
     */
    public String getIframeId() {
        HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext()
                .getRequest();
        String iFrameId = (String) request.getAttribute("sakai.tool.placement.id");
        return iFrameId;
    }

    public void setIframeId(String iframeId) {
        this.iframeId = iframeId;
    }

    public String getCustomLocation() {
        return customLocation;
    }

    public void setCustomLocation(String customLocation) {
        this.customLocation = customLocation;
    }

    public String getCustomCategory() {
        return customCategory;
    }

    public void setCustomCategory(String customCategory) {
        this.customCategory = customCategory;
    }

}