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