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