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