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