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