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