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