/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.calendar.util; import com.liferay.calendar.exporter.CalendarDataHandler; import com.liferay.calendar.model.Calendar; import com.liferay.calendar.model.CalendarBooking; import com.liferay.calendar.model.CalendarBookingConstants; import com.liferay.calendar.model.CalendarResource; import com.liferay.calendar.notification.NotificationType; import com.liferay.calendar.recurrence.Recurrence; import com.liferay.calendar.service.CalendarBookingLocalServiceUtil; import com.liferay.calendar.service.CalendarBookingServiceUtil; import com.liferay.calendar.service.CalendarLocalServiceUtil; import com.liferay.calendar.workflow.CalendarBookingWorkflowConstants; import com.liferay.portal.kernel.io.unsync.UnsyncStringReader; import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter; import com.liferay.portal.kernel.model.Company; import com.liferay.portal.kernel.model.ModelHintsUtil; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.service.CompanyLocalServiceUtil; import com.liferay.portal.kernel.service.ServiceContext; import com.liferay.portal.kernel.service.UserLocalServiceUtil; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.ReleaseInfo; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Time; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.workflow.WorkflowConstants; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Locale; import java.util.Map; import net.fortuna.ical4j.data.CalendarBuilder; import net.fortuna.ical4j.data.CalendarOutputter; import net.fortuna.ical4j.model.Component; import net.fortuna.ical4j.model.ComponentList; import net.fortuna.ical4j.model.Date; import net.fortuna.ical4j.model.DateList; import net.fortuna.ical4j.model.DateTime; import net.fortuna.ical4j.model.Dur; import net.fortuna.ical4j.model.Parameter; import net.fortuna.ical4j.model.ParameterList; import net.fortuna.ical4j.model.Property; import net.fortuna.ical4j.model.PropertyList; import net.fortuna.ical4j.model.component.VAlarm; import net.fortuna.ical4j.model.component.VEvent; import net.fortuna.ical4j.model.parameter.Cn; import net.fortuna.ical4j.model.parameter.CuType; import net.fortuna.ical4j.model.parameter.PartStat; import net.fortuna.ical4j.model.parameter.Role; import net.fortuna.ical4j.model.parameter.Rsvp; import net.fortuna.ical4j.model.parameter.XParameter; import net.fortuna.ical4j.model.property.Action; import net.fortuna.ical4j.model.property.Attendee; import net.fortuna.ical4j.model.property.CalScale; import net.fortuna.ical4j.model.property.DateProperty; import net.fortuna.ical4j.model.property.Description; import net.fortuna.ical4j.model.property.DtEnd; import net.fortuna.ical4j.model.property.DtStart; import net.fortuna.ical4j.model.property.ExDate; import net.fortuna.ical4j.model.property.Location; import net.fortuna.ical4j.model.property.Method; import net.fortuna.ical4j.model.property.ProdId; import net.fortuna.ical4j.model.property.RRule; import net.fortuna.ical4j.model.property.Summary; import net.fortuna.ical4j.model.property.Trigger; import net.fortuna.ical4j.model.property.Uid; import net.fortuna.ical4j.model.property.Version; import net.fortuna.ical4j.model.property.XProperty; /** * @author Marcellus Tavares */ public class CalendarICalDataHandler implements CalendarDataHandler { @Override public String exportCalendar(long calendarId) throws Exception { int[] statuses = { CalendarBookingWorkflowConstants.STATUS_APPROVED, CalendarBookingWorkflowConstants.STATUS_MAYBE, CalendarBookingWorkflowConstants.STATUS_PENDING }; List<CalendarBooking> calendarBookings = CalendarBookingServiceUtil.getCalendarBookings( calendarId, statuses); net.fortuna.ical4j.model.Calendar iCalCalendar = toICalCalendar( calendarBookings); return toString(iCalCalendar); } @Override public String exportCalendarBooking(long calendarBookingId) throws Exception { List<CalendarBooking> calendarBookings = new ArrayList<>(); CalendarBooking calendarBooking = CalendarBookingLocalServiceUtil.getCalendarBooking( calendarBookingId); calendarBookings.add(calendarBooking); net.fortuna.ical4j.model.Calendar iCalCalendar = toICalCalendar( calendarBookings); return toString(iCalCalendar); } @Override public void importCalendar(long calendarId, String data) throws Exception { CalendarBuilder calendarBuilder = new CalendarBuilder(); UnsyncStringReader unsyncStringReader = new UnsyncStringReader(data); net.fortuna.ical4j.model.Calendar iCalCalendar = calendarBuilder.build( unsyncStringReader); List<VEvent> vEvents = iCalCalendar.getComponents(Component.VEVENT); for (VEvent vEvent : vEvents) { importICalEvent(calendarId, vEvent); } } protected void importICalEvent(long calendarId, VEvent vEvent) throws Exception { Calendar calendar = CalendarLocalServiceUtil.getCalendar(calendarId); // Title User user = UserLocalServiceUtil.getUser(calendar.getUserId()); Map<Locale, String> titleMap = new HashMap<>(); Summary summary = vEvent.getSummary(); if (summary != null) { String title = ModelHintsUtil.trimString( CalendarBooking.class.getName(), "title", summary.getValue()); titleMap.put(user.getLocale(), title); } // Description Map<Locale, String> descriptionMap = new HashMap<>(); Description description = vEvent.getDescription(); if (description != null) { descriptionMap.put(user.getLocale(), description.getValue()); } // Location String locationString = StringPool.BLANK; Location location = vEvent.getLocation(); if (location != null) { locationString = location.getValue(); } // Dates DtStart dtStart = vEvent.getStartDate(); Date startDate = dtStart.getDate(); DtEnd dtEnd = vEvent.getEndDate(); Date endDate = dtEnd.getDate(); // All day boolean allDay = false; if (isICalDateOnly(dtStart)) { allDay = true; long time = endDate.getTime(); endDate.setTime(time - 1); } // Recurrence RRule rrule = (RRule)vEvent.getProperty(Property.RRULE); String recurrence = StringPool.BLANK; if (rrule != null) { recurrence = StringUtil.trim(rrule.toString()); PropertyList propertyList = vEvent.getProperties(Property.EXDATE); if (!propertyList.isEmpty()) { StringBundler sb = new StringBundler(); sb.append(recurrence); sb.append(StringPool.NEW_LINE); sb.append(_EXDATE); Iterator<ExDate> iterator = propertyList.iterator(); while (iterator.hasNext()) { ExDate exDate = iterator.next(); DateList dateList = exDate.getDates(); ListIterator<Date> listIterator = dateList.listIterator(); while (listIterator.hasNext()) { Date date = listIterator.next(); java.util.Calendar jCalendar = JCalendarUtil.getJCalendar(date.getTime()); int year = jCalendar.get(java.util.Calendar.YEAR); int month = jCalendar.get(java.util.Calendar.MONTH) + 1; int day = jCalendar.get(java.util.Calendar.DATE); int hour = jCalendar.get( java.util.Calendar.HOUR_OF_DAY); int minute = jCalendar.get(java.util.Calendar.MINUTE); int second = jCalendar.get(java.util.Calendar.SECOND); sb.append( String.format( _EXDATE_FORMAT, year, month, day, hour, minute, second)); if (listIterator.hasNext()) { sb.append(StringPool.COMMA); } } if (iterator.hasNext()) { sb.append(StringPool.COMMA); } } recurrence = sb.toString(); } } // Reminders ComponentList componentList = vEvent.getAlarms(); long[] reminders = new long[componentList.size()]; String[] reminderTypes = new String[componentList.size()]; int i = 0; for (Iterator<VAlarm> iterator = componentList.iterator(); iterator.hasNext();) { VAlarm vAlarm = iterator.next(); Action action = vAlarm.getAction(); String value = StringUtil.lowerCase(action.getValue()); if (!isActionSupported(value)) { continue; } reminderTypes[i] = value; Trigger trigger = vAlarm.getTrigger(); long time = 0; DateTime dateTime = trigger.getDateTime(); Dur dur = trigger.getDuration(); if ((dateTime == null) && (dur == null)) { continue; } if (dateTime != null) { time = startDate.getTime() - dateTime.getTime(); if (time < 0) { continue; } } else { if (!dur.isNegative()) { continue; } time += dur.getWeeks() * Time.WEEK; time += dur.getDays() * Time.DAY; time += dur.getHours() * Time.HOUR; time += dur.getMinutes() * Time.MINUTE; time += dur.getSeconds() * Time.SECOND; } reminders[i] = time; i++; } long firstReminder = 0; String firstReminderType = null; long secondReminder = 0; String secondReminderType = null; if (i > 0) { firstReminder = reminders[0]; firstReminderType = reminderTypes[0]; } if (i > 1) { secondReminder = reminders[1]; secondReminderType = reminderTypes[1]; } // Attendees PropertyList propertyList = vEvent.getProperties(Property.ATTENDEE); List<Long> childCalendarIds = new ArrayList<>(); for (Iterator<Attendee> iterator = propertyList.iterator(); iterator.hasNext();) { Attendee attendee = iterator.next(); URI uri = attendee.getCalAddress(); if (uri == null) { continue; } User attendeeUser = UserLocalServiceUtil.fetchUserByEmailAddress( calendar.getCompanyId(), uri.getSchemeSpecificPart()); if ((attendeeUser == null) || (calendar.getUserId() == attendeeUser.getUserId())) { continue; } ServiceContext serviceContext = new ServiceContext(); serviceContext.setCompanyId(calendar.getCompanyId()); serviceContext.setScopeGroupId(calendar.getGroupId()); CalendarResource calendarResource = CalendarResourceUtil.getUserCalendarResource( attendeeUser.getUserId(), serviceContext); if (calendarResource == null) { continue; } childCalendarIds.add(calendarResource.getDefaultCalendarId()); } long[] childCalendarIdsArray = ArrayUtil.toArray( childCalendarIds.toArray(new Long[childCalendarIds.size()])); // Merge calendar booking CalendarBooking calendarBooking = null; String vEventUidValue = null; Uid uid = vEvent.getUid(); if (uid != null) { vEventUidValue = uid.getValue(); calendarBooking = CalendarBookingLocalServiceUtil.fetchCalendarBooking( calendarId, vEventUidValue); } ServiceContext serviceContext = new ServiceContext(); serviceContext.setAddGroupPermissions(true); serviceContext.setAddGuestPermissions(true); serviceContext.setAttribute("sendNotification", Boolean.FALSE); serviceContext.setAttribute("vEventUid", vEventUidValue); serviceContext.setScopeGroupId(calendar.getGroupId()); if (calendarBooking == null) { CalendarBookingServiceUtil.addCalendarBooking( calendarId, childCalendarIdsArray, CalendarBookingConstants.PARENT_CALENDAR_BOOKING_ID_DEFAULT, CalendarBookingConstants.RECURRING_CALENDAR_BOOKING_ID_DEFAULT, titleMap, descriptionMap, locationString, startDate.getTime(), endDate.getTime(), allDay, recurrence, firstReminder, firstReminderType, secondReminder, secondReminderType, serviceContext); } else { CalendarBookingServiceUtil.updateCalendarBooking( calendarBooking.getCalendarBookingId(), calendarId, childCalendarIdsArray, titleMap, descriptionMap, locationString, startDate.getTime(), endDate.getTime(), allDay, recurrence, firstReminder, firstReminderType, secondReminder, secondReminderType, serviceContext); } } protected boolean isActionSupported(String value) { try { NotificationType.parse(value); } catch (IllegalArgumentException iae) { return false; } return true; } protected boolean isICalDateOnly(DateProperty dateProperty) { Parameter valueParameter = dateProperty.getParameter(Parameter.VALUE); if (valueParameter == null) { return false; } String value = valueParameter.getValue(); if (value.equals("DATE")) { return true; } return false; } protected VAlarm toICalAlarm( NotificationType notificationType, long reminder, String emailAddress) { Dur dur = toICalDur(reminder); VAlarm vAlarm = new VAlarm(dur); PropertyList propertyList = vAlarm.getProperties(); Action action = Action.DISPLAY; if (notificationType == NotificationType.EMAIL) { URI uri = URI.create("mailto:".concat(emailAddress)); Attendee attendee = new Attendee(uri); action = Action.EMAIL; propertyList.add(attendee); propertyList.add(new Summary("Alarm Notification")); } propertyList.add(action); propertyList.add(new Description("This is an event reminder.")); return vAlarm; } protected Attendee toICalAttendee( String fullName, String emailAddress, int status) { Attendee attendee = new Attendee(); URI uri = URI.create("mailto:".concat(emailAddress)); attendee.setCalAddress(uri); Cn cn = new Cn(fullName); ParameterList parameters = attendee.getParameters(); parameters.add(cn); parameters.add(CuType.INDIVIDUAL); parameters.add(Role.REQ_PARTICIPANT); parameters.add(Rsvp.TRUE); if (status == WorkflowConstants.STATUS_APPROVED) { parameters.add(PartStat.ACCEPTED); } else { parameters.add(PartStat.NEEDS_ACTION); } return attendee; } protected net.fortuna.ical4j.model.Calendar toICalCalendar( List<CalendarBooking> calendarBookings) throws Exception { net.fortuna.ical4j.model.Calendar iCalCalendar = new net.fortuna.ical4j.model.Calendar(); PropertyList propertiesList = iCalCalendar.getProperties(); ProdId prodId = new ProdId( "-//Liferay Inc//Liferay Portal " + ReleaseInfo.getVersion() + "//EN"); propertiesList.add(prodId); propertiesList.add(Version.VERSION_2_0); propertiesList.add(CalScale.GREGORIAN); propertiesList.add(Method.PUBLISH); List<VEvent> vEvents = iCalCalendar.getComponents(); for (CalendarBooking calendarBooking : calendarBookings) { vEvents.add(toICalEvent(calendarBooking)); } return iCalCalendar; } protected DateTime toICalDateTime(long time) { DateTime dateTime = new DateTime(); dateTime.setTime(time); dateTime.setUtc(true); return dateTime; } protected Dur toICalDur(long reminder) { int weeks = (int)(reminder / Time.WEEK); if (weeks > 0) { return new Dur(weeks); } int days = (int)(reminder / Time.DAY); if (days > 0) { return new Dur(days, 0, 0, 0); } int hours = (int)(reminder / Time.HOUR); if (hours > 0) { return new Dur(0, hours, 0, 0); } int minutes = (int)(reminder / Time.MINUTE); if (minutes > 0) { return new Dur(0, 0, minutes, 0); } int seconds = (int)(reminder / Time.SECOND); if (seconds > 0) { return new Dur(0, 0, 0, seconds); } return null; } protected VEvent toICalEvent(CalendarBooking calendarBooking) throws Exception { VEvent vEvent = new VEvent(); PropertyList propertyList = vEvent.getProperties(); // UID Uid uid = new Uid(calendarBooking.getVEventUid()); propertyList.add(uid); // Dates if (calendarBooking.isAllDay()) { DtStart dtStart = new DtStart( new Date(calendarBooking.getStartTime())); propertyList.add(dtStart); java.util.Calendar endJCalendar = JCalendarUtil.getJCalendar( calendarBooking.getEndTime()); endJCalendar.add(java.util.Calendar.DAY_OF_MONTH, 1); DtEnd dtEnd = new DtEnd(new Date(endJCalendar.getTime())); propertyList.add(dtEnd); } else { DtStart dtStart = new DtStart( toICalDateTime(calendarBooking.getStartTime())); propertyList.add(dtStart); DtEnd dtEnd = new DtEnd( toICalDateTime(calendarBooking.getEndTime())); propertyList.add(dtEnd); } // Title User user = UserLocalServiceUtil.getUser(calendarBooking.getUserId()); Summary summary = new Summary( calendarBooking.getTitle(user.getLocale())); propertyList.add(summary); // Description Company company = CompanyLocalServiceUtil.getCompany( calendarBooking.getCompanyId()); String calendarBookingDescription = StringUtil.replace( calendarBooking.getDescription(user.getLocale()), new String[] {"href=\"/", "src=\"/"}, new String[] { "href=\"" + company.getPortalURL(calendarBooking.getGroupId()) + "/", "src=\"" + company.getPortalURL(calendarBooking.getGroupId()) + "/" }); Description description = new Description(calendarBookingDescription); propertyList.add(description); XProperty xProperty = new XProperty( "X-ALT-DESC", calendarBookingDescription); ParameterList parameters = xProperty.getParameters(); parameters.add(new XParameter("FMTTYPE", "text/html")); propertyList.add(xProperty); // Location Location location = new Location(calendarBooking.getLocation()); propertyList.add(location); // Recurrence String recurrence = calendarBooking.getRecurrence(); if (Validator.isNotNull(recurrence)) { int index = recurrence.indexOf(StringPool.NEW_LINE); if (index > 0) { recurrence = recurrence.substring(0, index); } String value = StringUtil.replace( recurrence, _RRULE, StringPool.BLANK); RRule rRule = new RRule(value); propertyList.add(rRule); ExDate exDate = toICalExDate(calendarBooking.getRecurrenceObj()); if (exDate != null) { propertyList.add(exDate); } } // Reminders ComponentList componentList = vEvent.getAlarms(); long firstReminder = calendarBooking.getFirstReminder(); if (firstReminder > 0) { VAlarm vAlarm = toICalAlarm( calendarBooking.getFirstReminderNotificationType(), firstReminder, user.getEmailAddress()); componentList.add(vAlarm); } long secondReminder = calendarBooking.getSecondReminder(); if (secondReminder > 0) { VAlarm alarm = toICalAlarm( calendarBooking.getSecondReminderNotificationType(), secondReminder, user.getEmailAddress()); componentList.add(alarm); } // Attendees List<CalendarBooking> childCalendarBookings = calendarBooking.getChildCalendarBookings(); for (CalendarBooking childCalendarBooking : childCalendarBookings) { CalendarResource calResource = childCalendarBooking.getCalendarResource(); if (!calResource.isUser() || (calendarBooking.getCalendarBookingId() == childCalendarBooking.getCalendarBookingId())) { continue; } User calResourceUser = UserLocalServiceUtil.getUser( calResource.getClassPK()); Attendee attendee = toICalAttendee( calResourceUser.getFullName(), calResourceUser.getEmailAddress(), childCalendarBooking.getStatus()); propertyList.add(attendee); } return vEvent; } protected ExDate toICalExDate(Recurrence recurrence) { List<java.util.Calendar> exceptionJCalendars = recurrence.getExceptionJCalendars(); if (exceptionJCalendars.isEmpty()) { return null; } DateList dateList = new DateList(); dateList.setUtc(true); for (java.util.Calendar exceptionJCalendar : exceptionJCalendars) { DateTime dateTime = toICalDateTime( exceptionJCalendar.getTimeInMillis()); dateList.add(dateTime); } ExDate exDate = new ExDate(dateList); return exDate; } protected String toString(net.fortuna.ical4j.model.Calendar iCalCalendar) throws Exception { CalendarOutputter calendarOutputter = new CalendarOutputter(); ComponentList componentList = iCalCalendar.getComponents(); if (componentList.isEmpty()) { calendarOutputter.setValidating(false); } UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter(); calendarOutputter.output(iCalCalendar, unsyncStringWriter); unsyncStringWriter.flush(); return unsyncStringWriter.toString(); } private static final String _EXDATE = "EXDATE;TZID=\"UTC\";VALUE=DATE-TIME:"; private static final String _EXDATE_FORMAT = "%04d%02d%02dT%02d%02d%02dZ"; private static final String _RRULE = "RRULE:"; }