001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.calendar.service.impl;
016    
017    import com.liferay.portal.im.AIMConnector;
018    import com.liferay.portal.im.ICQConnector;
019    import com.liferay.portal.im.MSNConnector;
020    import com.liferay.portal.im.YMConnector;
021    import com.liferay.portal.kernel.cal.DayAndPosition;
022    import com.liferay.portal.kernel.cal.Recurrence;
023    import com.liferay.portal.kernel.cal.TZSRecurrence;
024    import com.liferay.portal.kernel.exception.PortalException;
025    import com.liferay.portal.kernel.exception.SystemException;
026    import com.liferay.portal.kernel.io.unsync.UnsyncBufferedOutputStream;
027    import com.liferay.portal.kernel.json.JSONFactoryUtil;
028    import com.liferay.portal.kernel.json.JSONObject;
029    import com.liferay.portal.kernel.log.Log;
030    import com.liferay.portal.kernel.log.LogFactoryUtil;
031    import com.liferay.portal.kernel.mail.MailMessage;
032    import com.liferay.portal.kernel.search.Indexable;
033    import com.liferay.portal.kernel.search.IndexableType;
034    import com.liferay.portal.kernel.util.ArrayUtil;
035    import com.liferay.portal.kernel.util.CalendarFactoryUtil;
036    import com.liferay.portal.kernel.util.CalendarUtil;
037    import com.liferay.portal.kernel.util.CharPool;
038    import com.liferay.portal.kernel.util.ContentTypes;
039    import com.liferay.portal.kernel.util.FastDateFormatFactoryUtil;
040    import com.liferay.portal.kernel.util.FileUtil;
041    import com.liferay.portal.kernel.util.HtmlUtil;
042    import com.liferay.portal.kernel.util.LocaleUtil;
043    import com.liferay.portal.kernel.util.ReleaseInfo;
044    import com.liferay.portal.kernel.util.StreamUtil;
045    import com.liferay.portal.kernel.util.StringPool;
046    import com.liferay.portal.kernel.util.StringUtil;
047    import com.liferay.portal.kernel.util.Time;
048    import com.liferay.portal.kernel.util.TimeZoneUtil;
049    import com.liferay.portal.kernel.util.UnmodifiableList;
050    import com.liferay.portal.kernel.util.Validator;
051    import com.liferay.portal.kernel.uuid.PortalUUIDUtil;
052    import com.liferay.portal.kernel.workflow.WorkflowConstants;
053    import com.liferay.portal.model.Company;
054    import com.liferay.portal.model.Contact;
055    import com.liferay.portal.model.ModelHintsUtil;
056    import com.liferay.portal.model.ResourceConstants;
057    import com.liferay.portal.model.User;
058    import com.liferay.portal.service.ServiceContext;
059    import com.liferay.portal.util.PortalUtil;
060    import com.liferay.portal.util.PortletKeys;
061    import com.liferay.portal.util.PropsValues;
062    import com.liferay.portlet.asset.model.AssetEntry;
063    import com.liferay.portlet.asset.model.AssetLinkConstants;
064    import com.liferay.portlet.calendar.EventDurationException;
065    import com.liferay.portlet.calendar.EventEndDateException;
066    import com.liferay.portlet.calendar.EventStartDateException;
067    import com.liferay.portlet.calendar.EventTitleException;
068    import com.liferay.portlet.calendar.model.CalEvent;
069    import com.liferay.portlet.calendar.model.CalEventConstants;
070    import com.liferay.portlet.calendar.service.base.CalEventLocalServiceBaseImpl;
071    import com.liferay.portlet.calendar.social.CalendarActivityKeys;
072    import com.liferay.portlet.calendar.util.CalUtil;
073    import com.liferay.util.TimeZoneSensitive;
074    
075    import java.io.File;
076    import java.io.FileOutputStream;
077    import java.io.IOException;
078    import java.io.InputStream;
079    import java.io.OutputStream;
080    
081    import java.text.Format;
082    
083    import java.util.ArrayList;
084    import java.util.Arrays;
085    import java.util.Calendar;
086    import java.util.Date;
087    import java.util.Iterator;
088    import java.util.List;
089    import java.util.Locale;
090    import java.util.Map;
091    import java.util.TimeZone;
092    
093    import javax.mail.internet.InternetAddress;
094    
095    import javax.portlet.PortletPreferences;
096    
097    import net.fortuna.ical4j.data.CalendarBuilder;
098    import net.fortuna.ical4j.data.CalendarOutputter;
099    import net.fortuna.ical4j.data.ParserException;
100    import net.fortuna.ical4j.model.Component;
101    import net.fortuna.ical4j.model.DateTime;
102    import net.fortuna.ical4j.model.Dur;
103    import net.fortuna.ical4j.model.Parameter;
104    import net.fortuna.ical4j.model.Property;
105    import net.fortuna.ical4j.model.PropertyList;
106    import net.fortuna.ical4j.model.Recur;
107    import net.fortuna.ical4j.model.WeekDay;
108    import net.fortuna.ical4j.model.component.VEvent;
109    import net.fortuna.ical4j.model.property.CalScale;
110    import net.fortuna.ical4j.model.property.Comment;
111    import net.fortuna.ical4j.model.property.DateProperty;
112    import net.fortuna.ical4j.model.property.Description;
113    import net.fortuna.ical4j.model.property.DtEnd;
114    import net.fortuna.ical4j.model.property.DtStart;
115    import net.fortuna.ical4j.model.property.Location;
116    import net.fortuna.ical4j.model.property.Method;
117    import net.fortuna.ical4j.model.property.ProdId;
118    import net.fortuna.ical4j.model.property.RRule;
119    import net.fortuna.ical4j.model.property.Summary;
120    import net.fortuna.ical4j.model.property.Uid;
121    import net.fortuna.ical4j.model.property.Version;
122    
123    /**
124     * @author Brian Wing Shun Chan
125     * @author Bruno Farache
126     * @author Samuel Kong
127     * @author Ganesh Ram
128     * @author Brett Swaim
129     * @author Mate Thurzo
130     */
131    public class CalEventLocalServiceImpl extends CalEventLocalServiceBaseImpl {
132    
133            @Indexable(type = IndexableType.REINDEX)
134            public CalEvent addEvent(
135                            long userId, String title, String description, String location,
136                            int startDateMonth, int startDateDay, int startDateYear,
137                            int startDateHour, int startDateMinute, int durationHour,
138                            int durationMinute, boolean allDay, boolean timeZoneSensitive,
139                            String type, boolean repeating, TZSRecurrence recurrence,
140                            int remindBy, int firstReminder, int secondReminder,
141                            ServiceContext serviceContext)
142                    throws PortalException, SystemException {
143    
144                    // Event
145    
146                    User user = userPersistence.findByPrimaryKey(userId);
147                    long groupId = serviceContext.getScopeGroupId();
148                    Date now = new Date();
149    
150                    Locale locale = null;
151                    TimeZone timeZone = null;
152    
153                    if (timeZoneSensitive) {
154                            locale = user.getLocale();
155                            timeZone = user.getTimeZone();
156                    }
157                    else {
158                            locale = LocaleUtil.getDefault();
159                            timeZone = TimeZoneUtil.getDefault();
160                    }
161    
162                    Calendar startDate = CalendarFactoryUtil.getCalendar(timeZone, locale);
163    
164                    startDate.set(Calendar.MONTH, startDateMonth);
165                    startDate.set(Calendar.DATE, startDateDay);
166                    startDate.set(Calendar.YEAR, startDateYear);
167                    startDate.set(Calendar.HOUR_OF_DAY, startDateHour);
168                    startDate.set(Calendar.MINUTE, startDateMinute);
169                    startDate.set(Calendar.SECOND, 0);
170                    startDate.set(Calendar.MILLISECOND, 0);
171    
172                    if (allDay) {
173                            startDate.set(Calendar.HOUR_OF_DAY, 0);
174                            startDate.set(Calendar.MINUTE, 0);
175    
176                            durationHour = 24;
177                            durationMinute = 0;
178                    }
179    
180                    validate(
181                            title, startDateMonth, startDateDay, startDateYear, durationHour,
182                            durationMinute, allDay, repeating, recurrence);
183    
184                    long eventId = counterLocalService.increment();
185    
186                    CalEvent event = calEventPersistence.create(eventId);
187    
188                    event.setUuid(serviceContext.getUuid());
189                    event.setGroupId(groupId);
190                    event.setCompanyId(user.getCompanyId());
191                    event.setUserId(user.getUserId());
192                    event.setUserName(user.getFullName());
193                    event.setCreateDate(serviceContext.getCreateDate(now));
194                    event.setModifiedDate(serviceContext.getModifiedDate(now));
195                    event.setTitle(title);
196                    event.setDescription(description);
197                    event.setLocation(location);
198                    event.setStartDate(startDate.getTime());
199                    event.setEndDate(getEndDate(recurrence));
200                    event.setDurationHour(durationHour);
201                    event.setDurationMinute(durationMinute);
202                    event.setAllDay(allDay);
203                    event.setTimeZoneSensitive(timeZoneSensitive);
204                    event.setType(type);
205                    event.setRepeating(repeating);
206                    event.setRecurrenceObj(recurrence);
207                    event.setRemindBy(remindBy);
208                    event.setFirstReminder(firstReminder);
209                    event.setSecondReminder(secondReminder);
210                    event.setExpandoBridgeAttributes(serviceContext);
211    
212                    calEventPersistence.update(event);
213    
214                    // Resources
215    
216                    if (serviceContext.isAddGroupPermissions() ||
217                            serviceContext.isAddGuestPermissions()) {
218    
219                            addEventResources(
220                                    event, serviceContext.isAddGroupPermissions(),
221                                    serviceContext.isAddGuestPermissions());
222                    }
223                    else {
224                            addEventResources(
225                                    event, serviceContext.getGroupPermissions(),
226                                    serviceContext.getGuestPermissions());
227                    }
228    
229                    // Asset
230    
231                    updateAsset(
232                            userId, event, serviceContext.getAssetCategoryIds(),
233                            serviceContext.getAssetTagNames(),
234                            serviceContext.getAssetLinkEntryIds());
235    
236                    // Message boards
237    
238                    if (PropsValues.CALENDAR_EVENT_COMMENTS_ENABLED) {
239                            mbMessageLocalService.addDiscussionMessage(
240                                    userId, event.getUserName(), groupId, CalEvent.class.getName(),
241                                    event.getEventId(), WorkflowConstants.ACTION_PUBLISH);
242                    }
243    
244                    // Social
245    
246                    JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject();
247    
248                    extraDataJSONObject.put("title", event.getTitle());
249    
250                    socialActivityLocalService.addActivity(
251                            userId, groupId, CalEvent.class.getName(), eventId,
252                            CalendarActivityKeys.ADD_EVENT, extraDataJSONObject.toString(), 0);
253    
254                    // Pool
255    
256                    CalEventLocalUtil.clearEventsPool(event.getGroupId());
257    
258                    return event;
259            }
260    
261            /**
262             * @deprecated {@link #addEvent(long, String, String, String, int, int, int,
263             *             int, int, int, int, boolean, boolean, String, boolean,
264             *             TZSRecurrence, int, int, int, ServiceContext)}
265             */
266            @Indexable(type = IndexableType.REINDEX)
267            public CalEvent addEvent(
268                            long userId, String title, String description, String location,
269                            int startDateMonth, int startDateDay, int startDateYear,
270                            int startDateHour, int startDateMinute, int endDateMonth,
271                            int endDateDay, int endDateYear, int durationHour,
272                            int durationMinute, boolean allDay, boolean timeZoneSensitive,
273                            String type, boolean repeating, TZSRecurrence recurrence,
274                            int remindBy, int firstReminder, int secondReminder,
275                            ServiceContext serviceContext)
276                    throws PortalException, SystemException {
277    
278                    return addEvent(
279                            userId, title, description, location, startDateMonth, startDateDay,
280                            startDateYear, startDateHour, startDateMinute, durationHour,
281                            durationMinute, allDay, timeZoneSensitive, type, repeating,
282                            recurrence, remindBy, firstReminder, secondReminder,
283                            serviceContext);
284            }
285    
286            public void addEventResources(
287                            CalEvent event, boolean addGroupPermissions,
288                            boolean addGuestPermissions)
289                    throws PortalException, SystemException {
290    
291                    resourceLocalService.addResources(
292                            event.getCompanyId(), event.getGroupId(), event.getUserId(),
293                            CalEvent.class.getName(), event.getEventId(), false,
294                            addGroupPermissions, addGuestPermissions);
295            }
296    
297            public void addEventResources(
298                            CalEvent event, String[] groupPermissions,
299                            String[] guestPermissions)
300                    throws PortalException, SystemException {
301    
302                    resourceLocalService.addModelResources(
303                            event.getCompanyId(), event.getGroupId(), event.getUserId(),
304                            CalEvent.class.getName(), event.getEventId(), groupPermissions,
305                            guestPermissions);
306            }
307    
308            public void addEventResources(
309                            long eventId, boolean addGroupPermissions,
310                            boolean addGuestPermissions)
311                    throws PortalException, SystemException {
312    
313                    CalEvent event = calEventPersistence.findByPrimaryKey(eventId);
314    
315                    addEventResources(event, addGroupPermissions, addGuestPermissions);
316            }
317    
318            public void addEventResources(
319                            long eventId, String[] groupPermissions, String[] guestPermissions)
320                    throws PortalException, SystemException {
321    
322                    CalEvent event = calEventPersistence.findByPrimaryKey(eventId);
323    
324                    addEventResources(event, groupPermissions, guestPermissions);
325            }
326    
327            public void checkEvents() throws PortalException, SystemException {
328                    List<CalEvent> events = calEventFinder.findByFutureReminders();
329    
330                    for (CalEvent event : events) {
331                            User user = userPersistence.fetchByPrimaryKey(event.getUserId());
332    
333                            if (user == null) {
334                                    calEventLocalService.deleteEvent(event);
335    
336                                    continue;
337                            }
338    
339                            Calendar now = CalendarFactoryUtil.getCalendar(
340                                    user.getTimeZone(), user.getLocale());
341    
342                            if (!event.isTimeZoneSensitive()) {
343                                    Calendar temp = CalendarFactoryUtil.getCalendar();
344    
345                                    temp.setTime(Time.getDate(now));
346    
347                                    now = temp;
348                            }
349    
350                            Calendar startDate = null;
351    
352                            if (event.isTimeZoneSensitive()) {
353                                    startDate = CalendarFactoryUtil.getCalendar(
354                                            user.getTimeZone(), user.getLocale());
355                            }
356                            else {
357                                    startDate = CalendarFactoryUtil.getCalendar();
358                            }
359    
360                            if (event.isRepeating()) {
361                                    double daysToCheck = Math.ceil(
362                                            CalEventConstants.REMINDERS[
363                                                    CalEventConstants.REMINDERS.length - 1] /
364                                            Time.DAY);
365    
366                                    Calendar cal = (Calendar)now.clone();
367    
368                                    for (int i = 0; i <= daysToCheck; i++) {
369                                            Recurrence recurrence = event.getRecurrenceObj();
370    
371                                            Calendar tzICal = CalendarFactoryUtil.getCalendar(
372                                                    TimeZoneUtil.getTimeZone(StringPool.UTC));
373    
374                                            tzICal.set(
375                                                    cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
376                                                    cal.get(Calendar.DATE));
377    
378                                            Calendar recurrenceCal = getRecurrenceCal(
379                                                    cal, tzICal, event);
380    
381                                            if (recurrence.isInRecurrence(recurrenceCal)) {
382                                                    remindUser(event, user, recurrenceCal, now);
383                                            }
384    
385                                            cal.add(Calendar.DAY_OF_YEAR, 1);
386                                    }
387                            }
388                            else {
389                                    startDate.setTime(event.getStartDate());
390    
391                                    remindUser(event, user, startDate, now);
392                            }
393                    }
394            }
395    
396            @Indexable(type = IndexableType.DELETE)
397            public CalEvent deleteEvent(CalEvent event)
398                    throws PortalException, SystemException {
399    
400                    // Event
401    
402                    calEventPersistence.remove(event);
403    
404                    // Resources
405    
406                    resourceLocalService.deleteResource(
407                            event.getCompanyId(), CalEvent.class.getName(),
408                            ResourceConstants.SCOPE_INDIVIDUAL, event.getEventId());
409    
410                    // Subscriptions
411    
412                    subscriptionLocalService.deleteSubscriptions(
413                            event.getCompanyId(), CalEvent.class.getName(), event.getEventId());
414    
415                    // Asset
416    
417                    assetEntryLocalService.deleteEntry(
418                            CalEvent.class.getName(), event.getEventId());
419    
420                    // Expando
421    
422                    expandoValueLocalService.deleteValues(
423                            CalEvent.class.getName(), event.getEventId());
424    
425                    // Pool
426    
427                    CalEventLocalUtil.clearEventsPool(event.getGroupId());
428    
429                    return event;
430            }
431    
432            @Indexable(type = IndexableType.DELETE)
433            public CalEvent deleteEvent(long eventId)
434                    throws PortalException, SystemException {
435    
436                    CalEvent event = calEventPersistence.findByPrimaryKey(eventId);
437    
438                    deleteEvent(event);
439    
440                    return event;
441            }
442    
443            public void deleteEvents(long groupId)
444                    throws PortalException, SystemException {
445    
446                    List<CalEvent> events = calEventPersistence.findByGroupId(groupId);
447    
448                    for (CalEvent event : events) {
449                            calEventLocalService.deleteEvent(event);
450                    }
451            }
452    
453            public File exportEvent(long userId, long eventId)
454                    throws PortalException, SystemException {
455    
456                    List<CalEvent> events = new ArrayList<CalEvent>();
457    
458                    CalEvent event = calEventPersistence.findByPrimaryKey(eventId);
459    
460                    events.add(event);
461    
462                    return exportEvents(userId, events, null);
463            }
464    
465            public File exportEvents(
466                            long userId, List<CalEvent> events, String fileName)
467                    throws PortalException, SystemException {
468    
469                    return exportICal4j(toICalCalendar(userId, events), fileName);
470            }
471    
472            public File exportGroupEvents(long userId, long groupId, String fileName)
473                    throws PortalException, SystemException {
474    
475                    List<CalEvent> events = calEventPersistence.findByGroupId(groupId);
476    
477                    return exportICal4j(toICalCalendar(userId, events), fileName);
478            }
479    
480            public List<CalEvent> getCompanyEvents(long companyId, int start, int end)
481                    throws SystemException {
482    
483                    return calEventPersistence.findByCompanyId(companyId, start, end);
484            }
485    
486            public int getCompanyEventsCount(long companyId) throws SystemException {
487                    return calEventPersistence.countByCompanyId(companyId);
488            }
489    
490            public CalEvent getEvent(long eventId)
491                    throws PortalException, SystemException {
492    
493                    return calEventPersistence.findByPrimaryKey(eventId);
494            }
495    
496            public List<CalEvent> getEvents(long groupId, Calendar cal)
497                    throws SystemException {
498    
499                    return getEvents(groupId, cal, new String[0]);
500            }
501    
502            public List<CalEvent> getEvents(long groupId, Calendar cal, String type)
503                    throws SystemException {
504    
505                    return getEvents(groupId, cal, new String[] {type});
506            }
507    
508            public List<CalEvent> getEvents(long groupId, Calendar cal, String[] types)
509                    throws SystemException {
510    
511                    if (types != null) {
512                            types = ArrayUtil.distinct(types);
513    
514                            Arrays.sort(types);
515                    }
516    
517                    Map<String, List<CalEvent>> eventsPool =
518                            CalEventLocalUtil.getEventsPool(groupId);
519    
520                    String key = CalUtil.toString(cal, types);
521    
522                    List<CalEvent> events = eventsPool.get(key);
523    
524                    if (events == null) {
525    
526                            // Time zone sensitive
527    
528                            List<CalEvent> events1 = calEventFinder.findByG_SD_T(
529                                    groupId, CalendarUtil.getGTDate(cal),
530                                    CalendarUtil.getLTDate(cal), true, types);
531    
532                            // Time zone insensitive
533    
534                            Calendar tzICal = CalendarFactoryUtil.getCalendar(
535                                    TimeZoneUtil.getTimeZone(StringPool.UTC));
536    
537                            tzICal.set(
538                                    cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
539                                    cal.get(Calendar.DATE));
540    
541                            List<CalEvent> events2 = calEventFinder.findByG_SD_T(
542                                    groupId, CalendarUtil.getGTDate(tzICal),
543                                    CalendarUtil.getLTDate(tzICal), false, types);
544    
545                            // Create new list
546    
547                            events = new ArrayList<CalEvent>();
548    
549                            events.addAll(events1);
550                            events.addAll(events2);
551    
552                            // Add repeating events
553    
554                            events.addAll(getRepeatingEvents(groupId, cal, types));
555    
556                            events = new UnmodifiableList<CalEvent>(events);
557    
558                            eventsPool.put(key, events);
559                    }
560    
561                    return events;
562            }
563    
564            public List<CalEvent> getEvents(
565                            long groupId, String type, int start, int end)
566                    throws SystemException {
567    
568                    return getEvents(groupId, new String[] {type}, start, end);
569            }
570    
571            public List<CalEvent> getEvents(
572                            long groupId, String[] types, int start, int end)
573                    throws SystemException {
574    
575                    if ((types != null) && (types.length > 0) &&
576                            ((types.length > 1) || Validator.isNotNull(types[0]))) {
577    
578                            return calEventPersistence.findByG_T(groupId, types, start, end);
579                    }
580                    else {
581                            return calEventPersistence.findByGroupId(groupId, start, end);
582                    }
583            }
584    
585            public int getEventsCount(long groupId, String type)
586                    throws SystemException {
587    
588                    return getEventsCount(groupId, new String[] {type});
589            }
590    
591            public int getEventsCount(long groupId, String[] types)
592                    throws SystemException {
593    
594                    if ((types != null) && (types.length > 0) &&
595                            ((types.length > 1) || Validator.isNotNull(types[0]))) {
596    
597                            return calEventPersistence.countByG_T(groupId, types);
598                    }
599                    else {
600                            return calEventPersistence.countByGroupId(groupId);
601                    }
602            }
603    
604            public List<CalEvent> getNoAssetEvents() throws SystemException {
605                    return calEventFinder.findByNoAssets();
606            }
607    
608            public List<CalEvent> getRepeatingEvents(long groupId)
609                    throws SystemException {
610    
611                    return getRepeatingEvents(groupId, null, null);
612            }
613    
614            public List<CalEvent> getRepeatingEvents(
615                            long groupId, Calendar cal, String[] types)
616                    throws SystemException {
617    
618                    Map<String, List<CalEvent>> eventsPool =
619                            CalEventLocalUtil.getEventsPool(groupId);
620    
621                    String key = "recurrence".concat(CalUtil.toString(null, types));
622    
623                    List<CalEvent> events = eventsPool.get(key);
624    
625                    if (events == null) {
626                            if ((types != null) && (types.length > 0) &&
627                                    ((types.length > 1) || Validator.isNotNull(types[0]))) {
628    
629                                    events = calEventPersistence.findByG_T_R(groupId, types, true);
630                            }
631                            else {
632                                    events = calEventPersistence.findByG_R(groupId, true);
633                            }
634    
635                            events = new UnmodifiableList<CalEvent>(events);
636    
637                            eventsPool.put(key, events);
638                    }
639    
640                    if (cal != null) {
641    
642                            // Time zone insensitive
643    
644                            Calendar tzICal = CalendarFactoryUtil.getCalendar(
645                                    TimeZoneUtil.getTimeZone(StringPool.UTC));
646    
647                            tzICal.set(
648                                    cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
649                                    cal.get(Calendar.DATE));
650    
651                            List<CalEvent> repeatingEvents = new ArrayList<CalEvent>();
652    
653                            for (CalEvent event : events) {
654                                    TZSRecurrence recurrence = event.getRecurrenceObj();
655    
656                                    try {
657    
658                                            // LEP-3468
659    
660                                            if ((recurrence.getFrequency() !=
661                                                            Recurrence.NO_RECURRENCE) &&
662                                                    (recurrence.getInterval() <= 0)) {
663    
664                                                    recurrence.setInterval(1);
665    
666                                                    event.setRecurrenceObj(recurrence);
667    
668                                                    event = calEventPersistence.update(event);
669    
670                                                    recurrence = event.getRecurrenceObj();
671                                            }
672    
673                                            if (recurrence.isInRecurrence(
674                                                            getRecurrenceCal(cal, tzICal, event))) {
675    
676                                                    repeatingEvents.add(event);
677                                            }
678                                    }
679                                    catch (Exception e) {
680                                            _log.error(e, e);
681                                    }
682                            }
683    
684                            events = new UnmodifiableList<CalEvent>(repeatingEvents);
685                    }
686    
687                    return events;
688            }
689    
690            public boolean hasEvents(long groupId, Calendar cal)
691                    throws SystemException {
692    
693                    return hasEvents(groupId, cal, new String[0]);
694            }
695    
696            public boolean hasEvents(long groupId, Calendar cal, String type)
697                    throws SystemException {
698    
699                    return hasEvents(groupId, cal, new String[] {type});
700            }
701    
702            public boolean hasEvents(long groupId, Calendar cal, String[] types)
703                    throws SystemException {
704    
705                    List<CalEvent> events = getEvents(groupId, cal, types);
706    
707                    if (events.isEmpty()) {
708                            return false;
709                    }
710                    else {
711                            return true;
712                    }
713            }
714    
715            public void importICal4j(long userId, long groupId, InputStream inputStream)
716                    throws PortalException, SystemException {
717    
718                    try {
719                            CalendarBuilder builder = new CalendarBuilder();
720    
721                            net.fortuna.ical4j.model.Calendar calendar = builder.build(
722                                    inputStream);
723    
724                            List<VEvent> vEvents = calendar.getComponents(Component.VEVENT);
725    
726                            for (VEvent vEvent : vEvents) {
727                                    importICal4j(userId, groupId, vEvent);
728                            }
729                    }
730                    catch (IOException ioe) {
731                            throw new SystemException(ioe.getMessage(), ioe);
732                    }
733                    catch (ParserException pe) {
734                            throw new SystemException(pe.getMessage(), pe);
735                    }
736            }
737    
738            public void updateAsset(
739                            long userId, CalEvent event, long[] assetCategoryIds,
740                            String[] assetTagNames, long[] assetLinkEntryIds)
741                    throws PortalException, SystemException {
742    
743                    AssetEntry assetEntry = assetEntryLocalService.updateEntry(
744                            userId, event.getGroupId(), event.getCreateDate(),
745                            event.getModifiedDate(), CalEvent.class.getName(),
746                            event.getEventId(), event.getUuid(), 0, assetCategoryIds,
747                            assetTagNames, true, null, null, null, ContentTypes.TEXT_HTML,
748                            event.getTitle(), event.getDescription(), null, null, null, 0, 0,
749                            null, false);
750    
751                    assetLinkLocalService.updateLinks(
752                            userId, assetEntry.getEntryId(), assetLinkEntryIds,
753                            AssetLinkConstants.TYPE_RELATED);
754            }
755    
756            @Indexable(type = IndexableType.REINDEX)
757            public CalEvent updateEvent(
758                            long userId, long eventId, String title, String description,
759                            String location, int startDateMonth, int startDateDay,
760                            int startDateYear, int startDateHour, int startDateMinute,
761                            int durationHour, int durationMinute, boolean allDay,
762                            boolean timeZoneSensitive, String type, boolean repeating,
763                            TZSRecurrence recurrence, int remindBy, int firstReminder,
764                            int secondReminder, ServiceContext serviceContext)
765                    throws PortalException, SystemException {
766    
767                    // Event
768    
769                    User user = userPersistence.findByPrimaryKey(userId);
770    
771                    Locale locale = null;
772                    TimeZone timeZone = null;
773    
774                    if (timeZoneSensitive) {
775                            locale = user.getLocale();
776                            timeZone = user.getTimeZone();
777                    }
778                    else {
779                            locale = LocaleUtil.getDefault();
780                            timeZone = TimeZoneUtil.getDefault();
781                    }
782    
783                    Calendar startDate = CalendarFactoryUtil.getCalendar(timeZone, locale);
784    
785                    startDate.set(Calendar.MONTH, startDateMonth);
786                    startDate.set(Calendar.DATE, startDateDay);
787                    startDate.set(Calendar.YEAR, startDateYear);
788                    startDate.set(Calendar.HOUR_OF_DAY, startDateHour);
789                    startDate.set(Calendar.MINUTE, startDateMinute);
790                    startDate.set(Calendar.SECOND, 0);
791                    startDate.set(Calendar.MILLISECOND, 0);
792    
793                    if (allDay) {
794                            startDate.set(Calendar.HOUR_OF_DAY, 0);
795                            startDate.set(Calendar.MINUTE, 0);
796    
797                            durationHour = 24;
798                            durationMinute = 0;
799                    }
800    
801                    validate(
802                            title, startDateMonth, startDateDay, startDateYear, durationHour,
803                            durationMinute, allDay, repeating, recurrence);
804    
805                    CalEvent event = calEventPersistence.findByPrimaryKey(eventId);
806    
807                    event.setModifiedDate(serviceContext.getModifiedDate(null));
808                    event.setTitle(title);
809                    event.setDescription(description);
810                    event.setLocation(location);
811                    event.setStartDate(startDate.getTime());
812                    event.setEndDate(getEndDate(recurrence));
813                    event.setDurationHour(durationHour);
814                    event.setDurationMinute(durationMinute);
815                    event.setAllDay(allDay);
816                    event.setTimeZoneSensitive(timeZoneSensitive);
817                    event.setType(type);
818                    event.setRepeating(repeating);
819                    event.setRecurrenceObj(recurrence);
820                    event.setRemindBy(remindBy);
821                    event.setFirstReminder(firstReminder);
822                    event.setSecondReminder(secondReminder);
823                    event.setExpandoBridgeAttributes(serviceContext);
824    
825                    calEventPersistence.update(event);
826    
827                    // Asset
828    
829                    updateAsset(
830                            userId, event, serviceContext.getAssetCategoryIds(),
831                            serviceContext.getAssetTagNames(),
832                            serviceContext.getAssetLinkEntryIds());
833    
834                    // Social
835    
836                    JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject();
837    
838                    extraDataJSONObject.put("title", event.getTitle());
839    
840                    socialActivityLocalService.addActivity(
841                            userId, event.getGroupId(), CalEvent.class.getName(), eventId,
842                            CalendarActivityKeys.UPDATE_EVENT, extraDataJSONObject.toString(),
843                            0);
844    
845                    // Pool
846    
847                    CalEventLocalUtil.clearEventsPool(event.getGroupId());
848    
849                    return event;
850            }
851    
852            /**
853             * @deprecated {@link #updateEvent(long, long, String, String, String, int,
854             *             int, int, int, int, int, int, boolean, boolean, String,
855             *             boolean, TZSRecurrence, int, int, int, ServiceContext)}
856             */
857            @Indexable(type = IndexableType.REINDEX)
858            public CalEvent updateEvent(
859                            long userId, long eventId, String title, String description,
860                            String location, int startDateMonth, int startDateDay,
861                            int startDateYear, int startDateHour, int startDateMinute,
862                            int endDateMonth, int endDateDay, int endDateYear, int durationHour,
863                            int durationMinute, boolean allDay, boolean timeZoneSensitive,
864                            String type, boolean repeating, TZSRecurrence recurrence,
865                            int remindBy, int firstReminder, int secondReminder,
866                            ServiceContext serviceContext)
867                    throws PortalException, SystemException {
868    
869                    return updateEvent(
870                            userId, eventId, title, description, location, startDateMonth,
871                            startDateDay, startDateYear, startDateHour, startDateMinute,
872                            durationHour, durationMinute, allDay, timeZoneSensitive, type,
873                            repeating, recurrence, remindBy, firstReminder, secondReminder,
874                            serviceContext);
875            }
876    
877            protected File exportICal4j(
878                            net.fortuna.ical4j.model.Calendar cal, String fileName)
879                    throws SystemException {
880    
881                    OutputStream os = null;
882    
883                    try {
884                            String extension = ".ics";
885    
886                            if (Validator.isNull(fileName)) {
887                                    fileName = "liferay_calendar.";
888                            }
889                            else {
890                                    int pos = fileName.lastIndexOf(CharPool.PERIOD);
891    
892                                    if (pos != -1) {
893                                            extension = fileName.substring(pos);
894                                            fileName = fileName.substring(0, pos);
895                                    }
896                            }
897    
898                            fileName = FileUtil.getShortFileName(fileName);
899    
900                            File file = File.createTempFile(fileName, extension);
901    
902                            os = new UnsyncBufferedOutputStream(
903                                    new FileOutputStream(file.getPath()));
904    
905                            CalendarOutputter calOutput = new CalendarOutputter();
906    
907                            if (cal.getComponents().isEmpty()) {
908                                    calOutput.setValidating(false);
909                            }
910    
911                            calOutput.output(cal, os);
912    
913                            return file;
914                    }
915                    catch (Exception e) {
916                            _log.error(e, e);
917    
918                            throw new SystemException(e);
919                    }
920                    finally {
921                            StreamUtil.cleanUp(os);
922                    }
923            }
924    
925            protected Date getEndDate(Recurrence recurrence) {
926                    if (recurrence == null) {
927                            return null;
928                    }
929    
930                    Calendar untilCalendar = recurrence.getUntil();
931    
932                    if (untilCalendar == null) {
933                            return null;
934                    }
935    
936                    return untilCalendar.getTime();
937            }
938    
939            protected Calendar getRecurrenceCal(
940                    Calendar cal, Calendar tzICal, CalEvent event) {
941    
942                    Calendar eventCal = CalendarFactoryUtil.getCalendar(
943                            TimeZoneUtil.getTimeZone(StringPool.UTC));
944    
945                    eventCal.setTime(event.getStartDate());
946    
947                    Calendar recurrenceCal = (Calendar)tzICal.clone();
948                    recurrenceCal.set(
949                            Calendar.HOUR_OF_DAY, eventCal.get(Calendar.HOUR_OF_DAY));
950                    recurrenceCal.set(Calendar.MINUTE, eventCal.get(Calendar.MINUTE));
951                    recurrenceCal.set(Calendar.SECOND, 0);
952                    recurrenceCal.set(Calendar.MILLISECOND, 0);
953    
954                    if (event.isTimeZoneSensitive()) {
955                            int gmtDate = eventCal.get(Calendar.DATE);
956                            long gmtMills = eventCal.getTimeInMillis();
957    
958                            eventCal.setTimeZone(cal.getTimeZone());
959    
960                            int tziDate = eventCal.get(Calendar.DATE);
961                            long tziMills = Time.getDate(eventCal).getTime();
962    
963                            if (gmtDate != tziDate) {
964                                    int diffDate = 0;
965    
966                                    if (gmtMills > tziMills) {
967                                            diffDate = (int)Math.ceil(
968                                                    (double)(gmtMills - tziMills) / Time.DAY);
969                                    }
970                                    else {
971                                            diffDate = (int)Math.floor(
972                                                    (double)(gmtMills - tziMills) / Time.DAY);
973                                    }
974    
975                                    recurrenceCal.add(Calendar.DATE, diffDate);
976                            }
977                    }
978    
979                    return recurrenceCal;
980            }
981    
982            protected void importICal4j(long userId, long groupId, VEvent event)
983                    throws PortalException, SystemException {
984    
985                    User user = userPersistence.findByPrimaryKey(userId);
986    
987                    TimeZone timeZone = user.getTimeZone();
988    
989                    // X iCal property
990    
991                    Property timeZoneXProperty = event.getProperty(
992                            TimeZoneSensitive.PROPERTY_NAME);
993    
994                    boolean timeZoneXPropertyValue = true;
995    
996                    if ((timeZoneXProperty != null) &&
997                            timeZoneXProperty.getValue().equals("FALSE")) {
998    
999                            timeZoneXPropertyValue = false;
1000                    }
1001    
1002                    // Title
1003    
1004                    String title = StringPool.BLANK;
1005    
1006                    if (event.getSummary() != null) {
1007                            title = ModelHintsUtil.trimString(
1008                                    CalEvent.class.getName(), "title",
1009                                    event.getSummary().getValue());
1010                    }
1011    
1012                    // Description
1013    
1014                    String description = StringPool.BLANK;
1015    
1016                    if (event.getDescription() != null) {
1017                            description = event.getDescription().getValue();
1018                    }
1019    
1020                    // Location
1021    
1022                    String location = StringPool.BLANK;
1023    
1024                    if (event.getLocation() != null) {
1025                            location = event.getLocation().getValue();
1026                    }
1027    
1028                    // Start date
1029    
1030                    DtStart dtStart = event.getStartDate();
1031    
1032                    Calendar startDate = toCalendar(
1033                            dtStart, timeZone, timeZoneXPropertyValue);
1034    
1035                    startDate.setTime(dtStart.getDate());
1036    
1037                    // End date
1038    
1039                    DtEnd dtEnd = event.getEndDate(true);
1040    
1041                    RRule rrule = (RRule)event.getProperty(Property.RRULE);
1042    
1043                    // Duration
1044    
1045                    long diffMillis = 0;
1046                    long durationHours = 24;
1047                    long durationMins = 0;
1048                    boolean multiDayEvent = false;
1049    
1050                    if (dtEnd != null) {
1051                            diffMillis =
1052                                    dtEnd.getDate().getTime() - startDate.getTimeInMillis();
1053                            durationHours = diffMillis / Time.HOUR;
1054                            durationMins = (diffMillis / Time.MINUTE) - (durationHours * 60);
1055    
1056                            if ((durationHours > 24) ||
1057                                    ((durationHours == 24) && (durationMins > 0))) {
1058    
1059                                    durationHours = 24;
1060                                    durationMins = 0;
1061                                    multiDayEvent = true;
1062                            }
1063                    }
1064    
1065                    // All day
1066    
1067                    boolean allDay = false;
1068    
1069                    if (isICal4jDateOnly(event.getStartDate()) || multiDayEvent) {
1070                            allDay = true;
1071                    }
1072    
1073                    // Time zone sensitive
1074    
1075                    boolean timeZoneSensitive = true;
1076    
1077                    if (allDay || !timeZoneXPropertyValue) {
1078                            timeZoneSensitive = false;
1079                    }
1080    
1081                    // Type
1082    
1083                    String type = StringPool.BLANK;
1084    
1085                    Property comment = event.getProperty(Property.COMMENT);
1086    
1087                    if ((comment != null) &&
1088                            ArrayUtil.contains(CalEventConstants.TYPES, comment.getValue())) {
1089    
1090                            type = comment.getValue();
1091                    }
1092    
1093                    // Recurrence
1094    
1095                    boolean repeating = false;
1096                    TZSRecurrence recurrence = null;
1097    
1098                    if (multiDayEvent) {
1099                            repeating = true;
1100    
1101                            Calendar recStartCal = CalendarFactoryUtil.getCalendar(
1102                                    TimeZoneUtil.getTimeZone(StringPool.UTC));
1103    
1104                            recStartCal.setTime(startDate.getTime());
1105    
1106                            com.liferay.portal.kernel.cal.Duration duration =
1107                                    new com.liferay.portal.kernel.cal.Duration(1, 0, 0, 0);
1108    
1109                            recurrence = new TZSRecurrence(
1110                                    recStartCal, duration, Recurrence.DAILY);
1111    
1112                            Calendar until = CalendarFactoryUtil.getCalendar(
1113                                    TimeZoneUtil.getTimeZone(StringPool.UTC));
1114    
1115                            until.setTimeInMillis(until.getTimeInMillis() + diffMillis);
1116    
1117                            recurrence.setUntil(until);
1118                    }
1119                    else if (rrule != null) {
1120                            repeating = true;
1121                            recurrence = toRecurrence(rrule, startDate);
1122                    }
1123    
1124                    // Reminder
1125    
1126                    int remindBy = CalEventConstants.REMIND_BY_NONE;
1127                    int firstReminder = 300000;
1128                    int secondReminder = 300000;
1129    
1130                    // Permissions
1131    
1132                    ServiceContext serviceContext = new ServiceContext();
1133    
1134                    serviceContext.setAddGroupPermissions(true);
1135                    serviceContext.setAddGuestPermissions(true);
1136                    serviceContext.setScopeGroupId(groupId);
1137    
1138                    // Merge event
1139    
1140                    String uuid = null;
1141    
1142                    CalEvent existingEvent = null;
1143    
1144                    if (event.getUid() != null) {
1145                            Uid uid = event.getUid();
1146    
1147                            if (existingEvent == null) {
1148    
1149                                    // VEvent exported by Liferay portal
1150    
1151                                    uuid = uid.getValue();
1152    
1153                                    existingEvent = calEventPersistence.fetchByUUID_G(
1154                                            uuid, groupId);
1155                            }
1156    
1157                            if (existingEvent == null) {
1158    
1159                                    // VEvent exported by external application
1160    
1161                                    uuid = PortalUUIDUtil.generate(uid.getValue().getBytes());
1162    
1163                                    existingEvent = calEventPersistence.fetchByUUID_G(
1164                                            uuid, groupId);
1165                            }
1166                    }
1167    
1168                    int startDateMonth = startDate.get(Calendar.MONTH);
1169                    int startDateDay = startDate.get(Calendar.DAY_OF_MONTH);
1170                    int startDateYear = startDate.get(Calendar.YEAR);
1171                    int startDateHour = startDate.get(Calendar.HOUR_OF_DAY);
1172                    int startDateMinute = startDate.get(Calendar.MINUTE);
1173                    int durationHour = (int)durationHours;
1174                    int durationMinute = (int)durationMins;
1175    
1176                    if (existingEvent == null) {
1177                            serviceContext.setUuid(uuid);
1178    
1179                            calEventLocalService.addEvent(
1180                                    userId, title, description, location, startDateMonth,
1181                                    startDateDay, startDateYear, startDateHour, startDateMinute,
1182                                    durationHour, durationMinute, allDay, timeZoneSensitive, type,
1183                                    repeating, recurrence, remindBy, firstReminder, secondReminder,
1184                                    serviceContext);
1185                    }
1186                    else {
1187                            calEventLocalService.updateEvent(
1188                                    userId, existingEvent.getEventId(), title, description,
1189                                    location, startDateMonth, startDateDay, startDateYear,
1190                                    startDateHour, startDateMinute, durationHour, durationMinute,
1191                                    allDay, timeZoneSensitive, type, repeating, recurrence,
1192                                    remindBy, firstReminder, secondReminder, serviceContext);
1193                    }
1194            }
1195    
1196            protected boolean isICal4jDateOnly(DateProperty dateProperty) {
1197                    Parameter valueParameter = dateProperty.getParameter(Parameter.VALUE);
1198    
1199                    if ((valueParameter != null) &&
1200                            valueParameter.getValue().equals("DATE")) {
1201    
1202                            return true;
1203                    }
1204    
1205                    return false;
1206            }
1207    
1208            protected void remindUser(CalEvent event, User user, Calendar startDate) {
1209                    int remindBy = event.getRemindBy();
1210    
1211                    if (remindBy == CalEventConstants.REMIND_BY_NONE) {
1212                            return;
1213                    }
1214    
1215                    try {
1216                            long ownerId = event.getGroupId();
1217                            int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
1218                            long plid = PortletKeys.PREFS_PLID_SHARED;
1219                            String portletId = PortletKeys.CALENDAR;
1220    
1221                            PortletPreferences preferences =
1222                                    portletPreferencesLocalService.getPreferences(
1223                                            event.getCompanyId(), ownerId, ownerType, plid, portletId);
1224    
1225                            if (!CalUtil.getEmailEventReminderEnabled(preferences)) {
1226                                    return;
1227                            }
1228    
1229                            Company company = companyPersistence.findByPrimaryKey(
1230                                    user.getCompanyId());
1231    
1232                            Contact contact = user.getContact();
1233    
1234                            String portletName = PortalUtil.getPortletTitle(
1235                                    PortletKeys.CALENDAR, user);
1236    
1237                            String fromName = CalUtil.getEmailFromName(
1238                                    preferences, event.getCompanyId());
1239                            String fromAddress = CalUtil.getEmailFromAddress(
1240                                    preferences, event.getCompanyId());
1241    
1242                            String toName = user.getFullName();
1243                            String toAddress = user.getEmailAddress();
1244    
1245                            if (remindBy == CalEventConstants.REMIND_BY_SMS) {
1246                                    toAddress = contact.getSmsSn();
1247                            }
1248    
1249                            String subject = CalUtil.getEmailEventReminderSubject(preferences);
1250                            String body = CalUtil.getEmailEventReminderBody(preferences);
1251    
1252                            Format dateFormatDateTime = FastDateFormatFactoryUtil.getDateTime(
1253                                    user.getLocale(), user.getTimeZone());
1254    
1255                            subject = StringUtil.replace(
1256                                    subject,
1257                                    new String[] {
1258                                            "[$EVENT_LOCATION$]", "[$EVENT_START_DATE$]",
1259                                            "[$EVENT_TITLE$]", "[$FROM_ADDRESS$]", "[$FROM_NAME$]",
1260                                            "[$PORTAL_URL$]", "[$PORTLET_NAME$]", "[$TO_ADDRESS$]",
1261                                            "[$TO_NAME$]"
1262                                    },
1263                                    new String[] {
1264                                            event.getLocation(),
1265                                            dateFormatDateTime.format(startDate.getTime()),
1266                                            event.getTitle(), fromAddress, fromName,
1267                                            company.getPortalURL(event.getGroupId()), portletName,
1268                                            HtmlUtil.escape(toAddress), HtmlUtil.escape(toName),
1269                                    });
1270    
1271                            body = StringUtil.replace(
1272                                    body,
1273                                    new String[] {
1274                                            "[$EVENT_LOCATION$]", "[$EVENT_START_DATE$]",
1275                                            "[$EVENT_TITLE$]", "[$FROM_ADDRESS$]", "[$FROM_NAME$]",
1276                                            "[$PORTAL_URL$]", "[$PORTLET_NAME$]", "[$TO_ADDRESS$]",
1277                                            "[$TO_NAME$]"
1278                                    },
1279                                    new String[] {
1280                                            event.getLocation(),
1281                                            dateFormatDateTime.format(startDate.getTime()),
1282                                            event.getTitle(), fromAddress, fromName,
1283                                            company.getPortalURL(event.getGroupId()), portletName,
1284                                            HtmlUtil.escape(toAddress), HtmlUtil.escape(toName),
1285                                    });
1286    
1287                            if ((remindBy == CalEventConstants.REMIND_BY_EMAIL) ||
1288                                    (remindBy == CalEventConstants.REMIND_BY_SMS)) {
1289    
1290                                    InternetAddress from = new InternetAddress(
1291                                            fromAddress, fromName);
1292    
1293                                    InternetAddress to = new InternetAddress(toAddress, toName);
1294    
1295                                    MailMessage message = new MailMessage(
1296                                            from, to, subject, body, true);
1297    
1298                                    mailService.sendEmail(message);
1299                            }
1300                            else if ((remindBy == CalEventConstants.REMIND_BY_AIM) &&
1301                                             Validator.isNotNull(contact.getAimSn())) {
1302    
1303                                    AIMConnector.send(contact.getAimSn(), body);
1304                            }
1305                            else if ((remindBy == CalEventConstants.REMIND_BY_ICQ) &&
1306                                             Validator.isNotNull(contact.getIcqSn())) {
1307    
1308                                    ICQConnector.send(contact.getIcqSn(), body);
1309                            }
1310                            else if ((remindBy == CalEventConstants.REMIND_BY_MSN) &&
1311                                             Validator.isNotNull(contact.getMsnSn())) {
1312    
1313                                    MSNConnector.send(contact.getMsnSn(), body);
1314                            }
1315                            else if ((remindBy == CalEventConstants.REMIND_BY_YM) &&
1316                                             Validator.isNotNull(contact.getYmSn())) {
1317    
1318                                    YMConnector.send(contact.getYmSn(), body);
1319                            }
1320                    }
1321                    catch (Exception e) {
1322                            _log.error(e, e);
1323                    }
1324            }
1325    
1326            protected void remindUser(
1327                    CalEvent event, User user, Calendar startCalendar,
1328                    Calendar nowCalendar) {
1329    
1330                    Date startDate = startCalendar.getTime();
1331    
1332                    long startTime = startDate.getTime();
1333    
1334                    Date nowDate = nowCalendar.getTime();
1335    
1336                    long nowTime = nowDate.getTime();
1337    
1338                    if (startTime < nowTime) {
1339                            return;
1340                    }
1341    
1342                    long diff = (startTime - nowTime) / _CALENDAR_EVENT_CHECK_INTERVAL;
1343    
1344                    if ((diff ==
1345                                    (event.getFirstReminder() / _CALENDAR_EVENT_CHECK_INTERVAL)) ||
1346                            (diff ==
1347                                    (event.getSecondReminder() / _CALENDAR_EVENT_CHECK_INTERVAL))) {
1348    
1349                            remindUser(event, user, startCalendar);
1350                    }
1351            }
1352    
1353            protected Calendar toCalendar(
1354                    DateProperty date, TimeZone timeZone, boolean timeZoneSensitive) {
1355    
1356                    Calendar cal = null;
1357    
1358                    if (isICal4jDateOnly(date)) {
1359                            cal = Calendar.getInstance();
1360                    }
1361                    else if (!timeZoneSensitive) {
1362                            cal = Calendar.getInstance(
1363                                    TimeZoneUtil.getTimeZone(StringPool.UTC));
1364                    }
1365                    else {
1366                            cal = Calendar.getInstance(timeZone);
1367                    }
1368    
1369                    return cal;
1370            }
1371    
1372            protected int toCalendarWeekDay(WeekDay weekDay) {
1373                    int dayOfWeeek = 0;
1374    
1375                    if (weekDay.getDay().equals(WeekDay.SU.getDay())) {
1376                            dayOfWeeek = Calendar.SUNDAY;
1377                    }
1378                    else if (weekDay.getDay().equals(WeekDay.MO.getDay())) {
1379                            dayOfWeeek = Calendar.MONDAY;
1380                    }
1381                    else if (weekDay.getDay().equals(WeekDay.TU.getDay())) {
1382                            dayOfWeeek = Calendar.TUESDAY;
1383                    }
1384                    else if (weekDay.getDay().equals(WeekDay.WE.getDay())) {
1385                            dayOfWeeek = Calendar.WEDNESDAY;
1386                    }
1387                    else if (weekDay.getDay().equals(WeekDay.TH.getDay())) {
1388                            dayOfWeeek = Calendar.THURSDAY;
1389                    }
1390                    else if (weekDay.getDay().equals(WeekDay.FR.getDay())) {
1391                            dayOfWeeek = Calendar.FRIDAY;
1392                    }
1393                    else if (weekDay.getDay().equals(WeekDay.SA.getDay())) {
1394                            dayOfWeeek = Calendar.SATURDAY;
1395                    }
1396    
1397                    return dayOfWeeek;
1398            }
1399    
1400            protected net.fortuna.ical4j.model.Calendar toICalCalendar(
1401                    long userId, List<CalEvent> events)
1402                    throws PortalException, SystemException {
1403    
1404                    net.fortuna.ical4j.model.Calendar iCal =
1405                            new net.fortuna.ical4j.model.Calendar();
1406    
1407                    ProdId prodId = new ProdId(
1408                            "-//Liferay Inc//Liferay Portal " + ReleaseInfo.getVersion() +
1409                            "//EN");
1410    
1411                    PropertyList propertiesList = iCal.getProperties();
1412    
1413                    propertiesList.add(prodId);
1414                    propertiesList.add(Version.VERSION_2_0);
1415                    propertiesList.add(CalScale.GREGORIAN);
1416    
1417                    // LPS-6058
1418    
1419                    propertiesList.add(Method.PUBLISH);
1420    
1421                    User user = userPersistence.findByPrimaryKey(userId);
1422                    TimeZone timeZone = user.getTimeZone();
1423    
1424                    List<VEvent> components = iCal.getComponents();
1425    
1426                    for (CalEvent event : events) {
1427                            components.add(toICalVEvent(event, timeZone));
1428                    }
1429    
1430                    return iCal;
1431            }
1432    
1433            protected Recur toICalRecurrence(TZSRecurrence recurrence) {
1434                    Recur recur = null;
1435    
1436                    int recurrenceType = recurrence.getFrequency();
1437    
1438                    int interval = recurrence.getInterval();
1439    
1440                    if (recurrenceType == Recurrence.DAILY) {
1441                            recur = new Recur(Recur.DAILY, -1);
1442    
1443                            if (interval >= 1) {
1444                                    recur.setInterval(interval);
1445                            }
1446    
1447                            DayAndPosition[] byDay = recurrence.getByDay();
1448    
1449                            if (byDay != null) {
1450                                    for (int i = 0; i < byDay.length; i++) {
1451                                            WeekDay weekDay = toICalWeekDay(byDay[i].getDayOfWeek());
1452    
1453                                            recur.getDayList().add(weekDay);
1454                                    }
1455                            }
1456    
1457                    }
1458                    else if (recurrenceType == Recurrence.WEEKLY) {
1459                            recur = new Recur(Recur.WEEKLY, -1);
1460    
1461                            recur.setInterval(interval);
1462    
1463                            DayAndPosition[] byDay = recurrence.getByDay();
1464    
1465                            if (byDay != null) {
1466                                    for (int i = 0; i < byDay.length; i++) {
1467                                            WeekDay weekDay = toICalWeekDay(byDay[i].getDayOfWeek());
1468    
1469                                            recur.getDayList().add(weekDay);
1470                                    }
1471                            }
1472                    }
1473                    else if (recurrenceType == Recurrence.MONTHLY) {
1474                            recur = new Recur(Recur.MONTHLY, -1);
1475    
1476                            recur.setInterval(interval);
1477    
1478                            int[] byMonthDay = recurrence.getByMonthDay();
1479    
1480                            if (byMonthDay != null) {
1481                                    Integer monthDay = new Integer(byMonthDay[0]);
1482    
1483                                    recur.getMonthDayList().add(monthDay);
1484                            }
1485                            else if (recurrence.getByDay() != null) {
1486                                    DayAndPosition[] byDay = recurrence.getByDay();
1487    
1488                                    WeekDay weekDay = toICalWeekDay(byDay[0].getDayOfWeek());
1489    
1490                                    recur.getDayList().add(weekDay);
1491    
1492                                    Integer position = new Integer(byDay[0].getDayPosition());
1493    
1494                                    recur.getSetPosList().add(position);
1495                            }
1496                    }
1497                    else if (recurrenceType == Recurrence.YEARLY) {
1498                            recur = new Recur(Recur.YEARLY, -1);
1499    
1500                            recur.setInterval(interval);
1501                    }
1502    
1503                    Calendar until = recurrence.getUntil();
1504    
1505                    if (until != null) {
1506                            DateTime dateTime = new DateTime(until.getTime());
1507    
1508                            recur.setUntil(dateTime);
1509                    }
1510    
1511                    return recur;
1512            }
1513    
1514            protected VEvent toICalVEvent(CalEvent event, TimeZone timeZone) {
1515                    VEvent vEvent = new VEvent();
1516    
1517                    PropertyList eventProps = vEvent.getProperties();
1518    
1519                    // UID
1520    
1521                    Uid uid = new Uid(event.getUuid());
1522    
1523                    eventProps.add(uid);
1524    
1525                    if (event.isAllDay()) {
1526    
1527                            // Start date
1528    
1529                            DtStart dtStart = new DtStart(
1530                                    new net.fortuna.ical4j.model.Date(event.getStartDate()));
1531    
1532                            eventProps.add(dtStart);
1533                    }
1534                    else {
1535    
1536                            // Start date
1537    
1538                            DtStart dtStart = new DtStart(new DateTime(event.getStartDate()));
1539    
1540                            eventProps.add(dtStart);
1541    
1542                            // Duration
1543    
1544                            Dur dur = new Dur(
1545                                    0, event.getDurationHour(), event.getDurationMinute(), 0);
1546    
1547                            DtEnd dtEnd = new DtEnd(
1548                                    new DateTime(dur.getTime(event.getStartDate())));
1549    
1550                            eventProps.add(dtEnd);
1551                    }
1552    
1553                    // Summary
1554    
1555                    Summary summary = new Summary(event.getTitle());
1556    
1557                    eventProps.add(summary);
1558    
1559                    // Description
1560    
1561                    Description description = new Description(event.getDescription());
1562    
1563                    eventProps.add(description);
1564    
1565                    // Location
1566    
1567                    Location location = new Location(event.getLocation());
1568    
1569                    eventProps.add(location);
1570    
1571                    // Comment
1572    
1573                    Comment comment = new Comment(event.getType());
1574    
1575                    eventProps.add(comment);
1576    
1577                    // Recurrence rule
1578    
1579                    if (event.isRepeating()) {
1580                            Recur recur = toICalRecurrence(event.getRecurrenceObj());
1581    
1582                            RRule rRule = new RRule(recur);
1583    
1584                            eventProps.add(rRule);
1585                    }
1586    
1587                    // Time zone sensitive
1588    
1589                    if (!event.getTimeZoneSensitive()) {
1590                            eventProps.add(new TimeZoneSensitive("FALSE"));
1591                    }
1592    
1593                    return vEvent;
1594            }
1595    
1596            protected WeekDay toICalWeekDay(int dayOfWeek) {
1597                    WeekDay weekDay = null;
1598    
1599                    if (dayOfWeek == Calendar.SUNDAY) {
1600                            weekDay = WeekDay.SU;
1601                    }
1602                    else if (dayOfWeek == Calendar.MONDAY) {
1603                            weekDay = WeekDay.MO;
1604                    }
1605                    else if (dayOfWeek == Calendar.TUESDAY) {
1606                            weekDay = WeekDay.TU;
1607                    }
1608                    else if (dayOfWeek == Calendar.WEDNESDAY) {
1609                            weekDay = WeekDay.WE;
1610                    }
1611                    else if (dayOfWeek == Calendar.THURSDAY) {
1612                            weekDay = WeekDay.TH;
1613                    }
1614                    else if (dayOfWeek == Calendar.FRIDAY) {
1615                            weekDay = WeekDay.FR;
1616                    }
1617                    else if (dayOfWeek == Calendar.SATURDAY) {
1618                            weekDay = WeekDay.SA;
1619                    }
1620    
1621                    return weekDay;
1622            }
1623    
1624            protected TZSRecurrence toRecurrence(RRule rRule, Calendar startDate) {
1625                    Recur recur = rRule.getRecur();
1626    
1627                    Calendar recStartCal = CalendarFactoryUtil.getCalendar(
1628                            TimeZoneUtil.getTimeZone(StringPool.UTC));
1629    
1630                    recStartCal.setTime(startDate.getTime());
1631    
1632                    TZSRecurrence recurrence = new TZSRecurrence(
1633                            recStartCal,
1634                            new com.liferay.portal.kernel.cal.Duration(1, 0, 0, 0));
1635    
1636                    recurrence.setWeekStart(Calendar.SUNDAY);
1637    
1638                    if (recur.getInterval() > 1) {
1639                            recurrence.setInterval(recur.getInterval());
1640                    }
1641    
1642                    Calendar until = Calendar.getInstance(
1643                            TimeZoneUtil.getTimeZone(StringPool.UTC));
1644    
1645                    String frequency = recur.getFrequency();
1646    
1647                    if (recur.getUntil() != null) {
1648                            until.setTime(recur.getUntil());
1649    
1650                            recurrence.setUntil(until);
1651                    }
1652                    else if (rRule.getValue().contains("COUNT")) {
1653                            until.setTimeInMillis(startDate.getTimeInMillis());
1654    
1655                            int addField = 0;
1656    
1657                            if (Recur.DAILY.equals(frequency)) {
1658                                    addField = Calendar.DAY_OF_YEAR;
1659                            }
1660                            else if (Recur.WEEKLY.equals(frequency)) {
1661                                    addField = Calendar.WEEK_OF_YEAR;
1662                            }
1663                            else if (Recur.MONTHLY.equals(frequency)) {
1664                                    addField = Calendar.MONTH;
1665                            }
1666                            else if (Recur.YEARLY.equals(frequency)) {
1667                                    addField = Calendar.YEAR;
1668                            }
1669    
1670                            int addAmount = recurrence.getInterval() * recur.getCount();
1671    
1672                            until.add(addField, addAmount);
1673                            until.add(Calendar.DAY_OF_YEAR, -1);
1674    
1675                            recurrence.setUntil(until);
1676                    }
1677    
1678                    if (Recur.DAILY.equals(frequency)) {
1679                            recurrence.setFrequency(Recurrence.DAILY);
1680    
1681                            List<DayAndPosition> dayPosList = new ArrayList<DayAndPosition>();
1682    
1683                            List<WeekDay> weekDays = recur.getDayList();
1684    
1685                            for (WeekDay weekDay : weekDays) {
1686                                    dayPosList.add(
1687                                            new DayAndPosition(toCalendarWeekDay(weekDay), 0));
1688                            }
1689    
1690                            if (!dayPosList.isEmpty()) {
1691                                    recurrence.setByDay(
1692                                            dayPosList.toArray(new DayAndPosition[dayPosList.size()]));
1693                            }
1694                    }
1695                    else if (Recur.WEEKLY.equals(frequency)) {
1696                            recurrence.setFrequency(Recurrence.WEEKLY);
1697    
1698                            List<DayAndPosition> dayPosList = new ArrayList<DayAndPosition>();
1699    
1700                            List<WeekDay> weekDays = recur.getDayList();
1701    
1702                            for (WeekDay weekDay : weekDays) {
1703                                    dayPosList.add(
1704                                            new DayAndPosition(toCalendarWeekDay(weekDay), 0));
1705                            }
1706    
1707                            if (!dayPosList.isEmpty()) {
1708                                    recurrence.setByDay(
1709                                            dayPosList.toArray(new DayAndPosition[dayPosList.size()]));
1710                            }
1711                    }
1712                    else if (Recur.MONTHLY.equals(frequency)) {
1713                            recurrence.setFrequency(Recurrence.MONTHLY);
1714    
1715                            Iterator<Integer> monthDayListItr =
1716                                    recur.getMonthDayList().iterator();
1717    
1718                            if (monthDayListItr.hasNext()) {
1719                                    Integer monthDay = monthDayListItr.next();
1720    
1721                                    recurrence.setByMonthDay(new int[] {monthDay.intValue()});
1722                            }
1723    
1724                            Iterator<WeekDay> dayListItr = recur.getDayList().iterator();
1725    
1726                            if (dayListItr.hasNext()) {
1727                                    WeekDay weekDay = dayListItr.next();
1728    
1729                                    DayAndPosition[] dayPos = {
1730                                            new DayAndPosition(toCalendarWeekDay(weekDay),
1731                                            weekDay.getOffset())
1732                                    };
1733    
1734                                    recurrence.setByDay(dayPos);
1735                            }
1736                    }
1737                    else if (Recur.YEARLY.equals(frequency)) {
1738                            recurrence.setFrequency(Recurrence.YEARLY);
1739                    }
1740    
1741                    return recurrence;
1742            }
1743    
1744            protected void validate(
1745                            String title, int startDateMonth, int startDateDay,
1746                            int startDateYear, int durationHour, int durationMinute,
1747                            boolean allDay, boolean repeating, TZSRecurrence recurrence)
1748                    throws PortalException {
1749    
1750                    if (Validator.isNull(title)) {
1751                            throw new EventTitleException();
1752                    }
1753    
1754                    if (!Validator.isDate(startDateMonth, startDateDay, startDateYear)) {
1755                            throw new EventStartDateException();
1756                    }
1757    
1758                    if (!allDay && (durationHour <= 0) && (durationMinute <= 0)) {
1759                            throw new EventDurationException();
1760                    }
1761    
1762                    Calendar startDate = CalendarFactoryUtil.getCalendar(
1763                            startDateYear, startDateMonth, startDateDay);
1764    
1765                    if (repeating) {
1766                            Calendar until = recurrence.getUntil();
1767    
1768                            if ((until != null) && startDate.after(until)) {
1769                                    throw new EventEndDateException();
1770                            }
1771                    }
1772            }
1773    
1774            private static final long _CALENDAR_EVENT_CHECK_INTERVAL =
1775                    PropsValues.CALENDAR_EVENT_CHECK_INTERVAL * Time.MINUTE;
1776    
1777            private static Log _log = LogFactoryUtil.getLog(
1778                    CalEventLocalServiceImpl.class);
1779    
1780    }