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