1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.calendar.service.impl;
24  
25  import com.liferay.portal.NoSuchUserException;
26  import com.liferay.portal.PortalException;
27  import com.liferay.portal.SystemException;
28  import com.liferay.portal.im.AIMConnector;
29  import com.liferay.portal.im.ICQConnector;
30  import com.liferay.portal.im.MSNConnector;
31  import com.liferay.portal.im.YMConnector;
32  import com.liferay.portal.kernel.cal.DayAndPosition;
33  import com.liferay.portal.kernel.cal.Recurrence;
34  import com.liferay.portal.kernel.mail.MailMessage;
35  import com.liferay.portal.kernel.util.Base64;
36  import com.liferay.portal.kernel.util.CalendarFactoryUtil;
37  import com.liferay.portal.kernel.util.LocaleUtil;
38  import com.liferay.portal.kernel.util.ReleaseInfo;
39  import com.liferay.portal.kernel.util.StringPool;
40  import com.liferay.portal.kernel.util.StringUtil;
41  import com.liferay.portal.kernel.util.TimeZoneUtil;
42  import com.liferay.portal.kernel.util.Validator;
43  import com.liferay.portal.kernel.uuid.PortalUUIDUtil;
44  import com.liferay.portal.model.Company;
45  import com.liferay.portal.model.Contact;
46  import com.liferay.portal.model.ResourceConstants;
47  import com.liferay.portal.model.User;
48  import com.liferay.portal.util.DateFormats;
49  import com.liferay.portal.util.PortalUtil;
50  import com.liferay.portal.util.PortletKeys;
51  import com.liferay.portlet.calendar.EventDurationException;
52  import com.liferay.portlet.calendar.EventEndDateException;
53  import com.liferay.portlet.calendar.EventStartDateException;
54  import com.liferay.portlet.calendar.EventTitleException;
55  import com.liferay.portlet.calendar.job.CheckEventJob;
56  import com.liferay.portlet.calendar.model.CalEvent;
57  import com.liferay.portlet.calendar.model.impl.CalEventImpl;
58  import com.liferay.portlet.calendar.service.base.CalEventLocalServiceBaseImpl;
59  import com.liferay.portlet.calendar.util.CalUtil;
60  import com.liferay.util.Time;
61  import com.liferay.util.cal.CalendarUtil;
62  import com.liferay.util.servlet.ServletResponseUtil;
63  
64  import java.io.BufferedOutputStream;
65  import java.io.File;
66  import java.io.FileOutputStream;
67  import java.io.FileReader;
68  import java.io.IOException;
69  import java.io.OutputStream;
70  
71  import java.text.DateFormat;
72  
73  import java.util.ArrayList;
74  import java.util.Calendar;
75  import java.util.Date;
76  import java.util.Iterator;
77  import java.util.List;
78  import java.util.Locale;
79  import java.util.Map;
80  import java.util.TimeZone;
81  
82  import javax.mail.internet.InternetAddress;
83  
84  import javax.portlet.PortletPreferences;
85  
86  import net.fortuna.ical4j.data.CalendarBuilder;
87  import net.fortuna.ical4j.data.CalendarOutputter;
88  import net.fortuna.ical4j.data.ParserException;
89  import net.fortuna.ical4j.model.Component;
90  import net.fortuna.ical4j.model.DateTime;
91  import net.fortuna.ical4j.model.Parameter;
92  import net.fortuna.ical4j.model.Property;
93  import net.fortuna.ical4j.model.PropertyList;
94  import net.fortuna.ical4j.model.Recur;
95  import net.fortuna.ical4j.model.WeekDay;
96  import net.fortuna.ical4j.model.component.VEvent;
97  import net.fortuna.ical4j.model.component.VTimeZone;
98  import net.fortuna.ical4j.model.property.CalScale;
99  import net.fortuna.ical4j.model.property.Comment;
100 import net.fortuna.ical4j.model.property.Description;
101 import net.fortuna.ical4j.model.property.DtStart;
102 import net.fortuna.ical4j.model.property.Duration;
103 import net.fortuna.ical4j.model.property.ProdId;
104 import net.fortuna.ical4j.model.property.RRule;
105 import net.fortuna.ical4j.model.property.Summary;
106 import net.fortuna.ical4j.model.property.Uid;
107 import net.fortuna.ical4j.model.property.Version;
108 
109 import org.apache.commons.logging.Log;
110 import org.apache.commons.logging.LogFactory;
111 
112 /**
113  * <a href="CalEventLocalServiceImpl.java.html"><b><i>View Source</i></b></a>
114  *
115  * @author Brian Wing Shun Chan
116  * @author Bruno Farache
117  *
118  */
119 public class CalEventLocalServiceImpl extends CalEventLocalServiceBaseImpl {
120 
121     public CalEvent addEvent(
122             long userId, long plid, String title, String description,
123             int startDateMonth, int startDateDay, int startDateYear,
124             int startDateHour, int startDateMinute, int endDateMonth,
125             int endDateDay, int endDateYear, int durationHour,
126             int durationMinute, boolean allDay, boolean timeZoneSensitive,
127             String type, boolean repeating, Recurrence recurrence,
128             String remindBy, int firstReminder, int secondReminder,
129             boolean addCommunityPermissions, boolean addGuestPermissions)
130         throws PortalException, SystemException {
131 
132         return addEvent(
133             null, userId, plid, title, description, startDateMonth,
134             startDateDay, startDateYear, startDateHour, startDateMinute,
135             endDateMonth, endDateDay, endDateYear, durationHour, durationMinute,
136             allDay, timeZoneSensitive, type, repeating, recurrence, remindBy,
137             firstReminder, secondReminder,
138             Boolean.valueOf(addCommunityPermissions),
139             Boolean.valueOf(addGuestPermissions), null, null);
140     }
141 
142     public CalEvent addEvent(
143             String uuid, long userId, long plid, String title,
144             String description, int startDateMonth, int startDateDay,
145             int startDateYear, int startDateHour, int startDateMinute,
146             int endDateMonth, int endDateDay, int endDateYear, int durationHour,
147             int durationMinute, boolean allDay, boolean timeZoneSensitive,
148             String type, boolean repeating, Recurrence recurrence,
149             String remindBy, int firstReminder, int secondReminder,
150             boolean addCommunityPermissions, boolean addGuestPermissions)
151         throws PortalException, SystemException {
152 
153         return addEvent(
154             uuid, userId, plid, title, description, startDateMonth,
155             startDateDay, startDateYear, startDateHour, startDateMinute,
156             endDateMonth, endDateDay, endDateYear, durationHour, durationMinute,
157             allDay, timeZoneSensitive, type, repeating, recurrence, remindBy,
158             firstReminder, secondReminder, new Boolean(addCommunityPermissions),
159             new Boolean(addGuestPermissions), null, null);
160     }
161 
162     public CalEvent addEvent(
163             long userId, long plid, String title, String description,
164             int startDateMonth, int startDateDay, int startDateYear,
165             int startDateHour, int startDateMinute, int endDateMonth,
166             int endDateDay, int endDateYear, int durationHour,
167             int durationMinute, boolean allDay, boolean timeZoneSensitive,
168             String type, boolean repeating, Recurrence recurrence,
169             String remindBy, int firstReminder, int secondReminder,
170             String[] communityPermissions, String[] guestPermissions)
171         throws PortalException, SystemException {
172 
173         return addEvent(
174             null, userId, plid, title, description, startDateMonth,
175             startDateDay, startDateYear, startDateHour, startDateMinute,
176             endDateMonth, endDateDay, endDateYear, durationHour, durationMinute,
177             allDay, timeZoneSensitive, type, repeating, recurrence, remindBy,
178             firstReminder, secondReminder, null, null, communityPermissions,
179             guestPermissions);
180     }
181 
182     public CalEvent addEvent(
183             String uuid, long userId, long plid, String title,
184             String description, int startDateMonth, int startDateDay,
185             int startDateYear, int startDateHour, int startDateMinute,
186             int endDateMonth, int endDateDay, int endDateYear, int durationHour,
187             int durationMinute, boolean allDay, boolean timeZoneSensitive,
188             String type, boolean repeating, Recurrence recurrence,
189             String remindBy, int firstReminder, int secondReminder,
190             Boolean addCommunityPermissions, Boolean addGuestPermissions,
191             String[] communityPermissions, String[] guestPermissions)
192         throws PortalException, SystemException {
193 
194         // Event
195 
196         User user = userPersistence.findByPrimaryKey(userId);
197         long groupId = PortalUtil.getPortletGroupId(plid);
198         Date now = new Date();
199 
200         Locale locale = null;
201         TimeZone timeZone = null;
202 
203         if (timeZoneSensitive) {
204             locale = user.getLocale();
205             timeZone = user.getTimeZone();
206         }
207         else {
208             locale = LocaleUtil.getDefault();
209             timeZone = TimeZoneUtil.getDefault();
210         }
211 
212         Calendar startDate = CalendarFactoryUtil.getCalendar(timeZone, locale);
213 
214         startDate.set(Calendar.MONTH, startDateMonth);
215         startDate.set(Calendar.DATE, startDateDay);
216         startDate.set(Calendar.YEAR, startDateYear);
217         startDate.set(Calendar.HOUR_OF_DAY, startDateHour);
218         startDate.set(Calendar.MINUTE, startDateMinute);
219         startDate.set(Calendar.SECOND, 0);
220         startDate.set(Calendar.MILLISECOND, 0);
221 
222         Calendar endDate = CalendarFactoryUtil.getCalendar(timeZone, locale);
223 
224         endDate.set(Calendar.MONTH, endDateMonth);
225         endDate.set(Calendar.DATE, endDateDay);
226         endDate.set(Calendar.YEAR, endDateYear);
227         endDate.set(Calendar.HOUR_OF_DAY, 23);
228         endDate.set(Calendar.MINUTE, 59);
229         endDate.set(Calendar.SECOND, 59);
230         endDate.set(Calendar.MILLISECOND, 999);
231 
232         if (allDay) {
233             startDate.set(Calendar.HOUR_OF_DAY, 0);
234             startDate.set(Calendar.MINUTE, 0);
235 
236             durationHour = 24;
237             durationMinute = 0;
238         }
239 
240         validate(
241             title, startDateMonth, startDateDay, startDateYear, endDateMonth,
242             endDateDay, endDateYear, durationHour, durationMinute, allDay);
243 
244         long eventId = counterLocalService.increment();
245 
246         CalEvent event = calEventPersistence.create(eventId);
247 
248         event.setUuid(uuid);
249         event.setGroupId(groupId);
250         event.setCompanyId(user.getCompanyId());
251         event.setUserId(user.getUserId());
252         event.setUserName(user.getFullName());
253         event.setCreateDate(now);
254         event.setModifiedDate(now);
255         event.setTitle(title);
256         event.setDescription(description);
257         event.setStartDate(startDate.getTime());
258         event.setEndDate(endDate.getTime());
259         event.setDurationHour(durationHour);
260         event.setDurationMinute(durationMinute);
261         event.setAllDay(allDay);
262         event.setTimeZoneSensitive(timeZoneSensitive);
263         event.setType(type);
264         event.setRepeating(repeating);
265         event.setRecurrence(Base64.objectToString(recurrence));
266         event.setRemindBy(remindBy);
267         event.setFirstReminder(firstReminder);
268         event.setSecondReminder(secondReminder);
269 
270         calEventPersistence.update(event, false);
271 
272         // Resources
273 
274         if ((addCommunityPermissions != null) &&
275             (addGuestPermissions != null)) {
276 
277             addEventResources(
278                 event, addCommunityPermissions.booleanValue(),
279                 addGuestPermissions.booleanValue());
280         }
281         else {
282             addEventResources(event, communityPermissions, guestPermissions);
283         }
284 
285         // Pool
286 
287         CalEventLocalUtil.clearEventsPool(event.getGroupId());
288 
289         return event;
290     }
291 
292     public void addEventResources(
293             long eventId, boolean addCommunityPermissions,
294             boolean addGuestPermissions)
295         throws PortalException, SystemException {
296 
297         CalEvent event = calEventPersistence.findByPrimaryKey(eventId);
298 
299         addEventResources(
300             event, addCommunityPermissions, addGuestPermissions);
301     }
302 
303     public void addEventResources(
304             CalEvent event, boolean addCommunityPermissions,
305             boolean addGuestPermissions)
306         throws PortalException, SystemException {
307 
308         resourceLocalService.addResources(
309             event.getCompanyId(), event.getGroupId(), event.getUserId(),
310             CalEvent.class.getName(), event.getEventId(), false,
311             addCommunityPermissions, addGuestPermissions);
312     }
313 
314     public void addEventResources(
315             long eventId, String[] communityPermissions,
316             String[] guestPermissions)
317         throws PortalException, SystemException {
318 
319         CalEvent event = calEventPersistence.findByPrimaryKey(eventId);
320 
321         addEventResources(event, communityPermissions, guestPermissions);
322     }
323 
324     public void addEventResources(
325             CalEvent event, String[] communityPermissions,
326             String[] guestPermissions)
327         throws PortalException, SystemException {
328 
329         resourceLocalService.addModelResources(
330             event.getCompanyId(), event.getGroupId(), event.getUserId(),
331             CalEvent.class.getName(), event.getEventId(), communityPermissions,
332             guestPermissions);
333     }
334 
335     public void checkEvents() throws PortalException, SystemException {
336         Iterator<CalEvent> itr = calEventFinder.findByRemindBy().iterator();
337 
338         while (itr.hasNext()) {
339             CalEvent event = itr.next();
340 
341             User user = userPersistence.findByPrimaryKey(event.getUserId());
342 
343             Calendar now = CalendarFactoryUtil.getCalendar(
344                 user.getTimeZone(), user.getLocale());
345 
346             if (!event.isTimeZoneSensitive()) {
347                 Calendar temp = CalendarFactoryUtil.getCalendar();
348 
349                 temp.setTime(Time.getDate(now));
350 
351                 now = temp;
352             }
353 
354             Calendar startDate = null;
355 
356             if (event.isTimeZoneSensitive()) {
357                 startDate = CalendarFactoryUtil.getCalendar(
358                     user.getTimeZone(), user.getLocale());
359             }
360             else {
361                 startDate = CalendarFactoryUtil.getCalendar();
362             }
363 
364             startDate.setTime(event.getStartDate());
365 
366             long diff =
367                 (startDate.getTime().getTime() - now.getTime().getTime()) /
368                 CheckEventJob.INTERVAL;
369 
370             if ((diff == (event.getFirstReminder() / CheckEventJob.INTERVAL)) ||
371                 (diff ==
372                     (event.getSecondReminder() / CheckEventJob.INTERVAL))) {
373 
374                 remindUser(event, user);
375             }
376         }
377     }
378 
379     public void deleteEvent(long eventId)
380         throws PortalException, SystemException {
381 
382         CalEvent event = calEventPersistence.findByPrimaryKey(eventId);
383 
384         deleteEvent(event);
385     }
386 
387     public void deleteEvent(CalEvent event)
388         throws PortalException, SystemException {
389 
390         // Pool
391 
392         CalEventLocalUtil.clearEventsPool(event.getGroupId());
393 
394         // Resources
395 
396         resourceLocalService.deleteResource(
397             event.getCompanyId(), CalEvent.class.getName(),
398             ResourceConstants.SCOPE_INDIVIDUAL, event.getEventId());
399 
400         // Event
401 
402         calEventPersistence.remove(event.getEventId());
403     }
404 
405     public void deleteEvents(long groupId)
406         throws PortalException, SystemException {
407 
408         Iterator<CalEvent> itr = calEventPersistence.findByGroupId(
409             groupId).iterator();
410 
411         while (itr.hasNext()) {
412             CalEvent event = itr.next();
413 
414             deleteEvent(event);
415         }
416     }
417 
418     public File exportEvent(long userId, long eventId)
419         throws PortalException, SystemException {
420 
421         List<CalEvent> events = new ArrayList<CalEvent>();
422 
423         CalEvent event = calEventPersistence.findByPrimaryKey(eventId);
424 
425         events.add(event);
426 
427         return exportICal4j(toICalCalendar(userId, events), null);
428     }
429 
430     public File exportGroupEvents(long userId, long plid, String fileName)
431         throws PortalException, SystemException {
432 
433         long groupId = PortalUtil.getPortletGroupId(plid);
434 
435         List<CalEvent> events = calEventPersistence.findByGroupId(groupId);
436 
437         return exportICal4j(toICalCalendar(userId, events), fileName);
438     }
439 
440     public CalEvent getEvent(long eventId)
441         throws PortalException, SystemException {
442 
443         return calEventPersistence.findByPrimaryKey(eventId);
444     }
445 
446     public List<CalEvent> getEvents(
447             long groupId, String type, int begin, int end)
448         throws SystemException {
449 
450         if (Validator.isNull(type)) {
451             return calEventPersistence.findByGroupId(groupId, begin, end);
452         }
453         else {
454             return calEventPersistence.findByG_T(groupId, type, begin, end);
455         }
456     }
457 
458     public List<CalEvent> getEvents(long groupId, Calendar cal)
459         throws SystemException {
460 
461         Map<String, List<CalEvent>> eventsPool =
462             CalEventLocalUtil.getEventsPool(groupId);
463 
464         String key = CalUtil.toString(cal);
465 
466         List<CalEvent> events = eventsPool.get(key);
467 
468         if (events == null) {
469 
470             // Time zone sensitive
471 
472             List<CalEvent> events1 = calEventFinder.findByG_SD(
473                 groupId, CalendarUtil.getGTDate(cal),
474                 CalendarUtil.getLTDate(cal), true);
475 
476             // Time zone insensitive
477 
478             Calendar tzICal = CalendarFactoryUtil.getCalendar(
479                 cal.get(Calendar.YEAR),
480                 cal.get(Calendar.MONTH),
481                 cal.get(Calendar.DATE));
482 
483             List<CalEvent> events2 = calEventFinder.findByG_SD(
484                 groupId, CalendarUtil.getGTDate(tzICal),
485                 CalendarUtil.getLTDate(tzICal), false);
486 
487             // Create new list
488 
489             events = new ArrayList<CalEvent>();
490 
491             events.addAll(events1);
492             events.addAll(events2);
493 
494             // Add repeating events
495 
496             Iterator<CalEvent> itr = getRepeatingEvents(groupId).iterator();
497 
498             while (itr.hasNext()) {
499                 CalEvent event = itr.next();
500 
501                 Recurrence recurrence = event.getRecurrenceObj();
502 
503                 try {
504 
505                     // LEP-3468
506 
507                     if ((recurrence.getFrequency() == Recurrence.DAILY) &&
508                         (recurrence.getInterval() <= 0)) {
509 
510                         recurrence.setInterval(1);
511 
512                         event.setRecurrence(Base64.objectToString(recurrence));
513 
514                         event = calEventPersistence.update(event, false);
515 
516                         recurrence = event.getRecurrenceObj();
517                     }
518 
519                     if (recurrence.isInRecurrence(
520                             getRecurrenceCal(cal, tzICal, event))) {
521 
522                         events.add(event);
523                     }
524                 }
525                 catch (Exception e) {
526                     _log.error(e.getMessage());
527                 }
528             }
529 
530             eventsPool.put(key, events);
531         }
532 
533         return events;
534     }
535 
536     public List<CalEvent> getEvents(long groupId, Calendar cal, String type)
537         throws SystemException {
538 
539         List<CalEvent> events = getEvents(groupId, cal);
540 
541         if (Validator.isNull(type)) {
542             return events;
543         }
544         else {
545             events = new ArrayList<CalEvent>(events);
546 
547             Iterator<CalEvent> itr = events.iterator();
548 
549             while (itr.hasNext()) {
550                 CalEvent event = itr.next();
551 
552                 if (!event.getType().equals(type)) {
553                     itr.remove();
554                 }
555             }
556 
557             return events;
558         }
559     }
560 
561     public int getEventsCount(long groupId, String type)
562         throws SystemException {
563 
564         if (Validator.isNull(type)) {
565             return calEventPersistence.countByGroupId(groupId);
566         }
567         else {
568             return calEventPersistence.countByG_T(groupId, type);
569         }
570     }
571 
572     public List<CalEvent> getRepeatingEvents(long groupId)
573         throws SystemException {
574 
575         Map<String, List<CalEvent>> eventsPool =
576             CalEventLocalUtil.getEventsPool(groupId);
577 
578         String key = "recurrence";
579 
580         List<CalEvent> events = eventsPool.get(key);
581 
582         if (events == null) {
583             events = calEventPersistence.findByG_R(groupId, true);
584 
585             eventsPool.put(key, events);
586         }
587 
588         return events;
589     }
590 
591     public boolean hasEvents(long groupId, Calendar cal)
592         throws SystemException {
593 
594         return hasEvents(groupId, cal, null);
595     }
596 
597     public boolean hasEvents(long groupId, Calendar cal, String type)
598         throws SystemException {
599 
600         if (getEvents(groupId, cal, type).size() > 0) {
601             return true;
602         }
603         else {
604             return false;
605         }
606     }
607 
608     public void importICal4j(long userId, long plid, File file)
609         throws PortalException, SystemException {
610 
611         try {
612             CalendarBuilder builder = new CalendarBuilder();
613 
614             net.fortuna.ical4j.model.Calendar calendar = builder.build(
615                 new FileReader(file));
616 
617             TimeZone timeZone = toTimeZone(
618                 userId, (VTimeZone)calendar.getComponent(Component.VTIMEZONE));
619 
620             Iterator<VEvent> itr = calendar.getComponents(
621                 Component.VEVENT).iterator();
622 
623             while (itr.hasNext()) {
624                 VEvent vEvent = itr.next();
625 
626                 importICal4j(userId, plid, vEvent, timeZone);
627             }
628         }
629         catch (IOException ioe) {
630             throw new SystemException(ioe.getMessage());
631         }
632         catch (ParserException pe) {
633             throw new SystemException(pe.getMessage());
634         }
635     }
636 
637     public CalEvent updateEvent(
638             long userId, long eventId, String title, String description,
639             int startDateMonth, int startDateDay, int startDateYear,
640             int startDateHour, int startDateMinute, int endDateMonth,
641             int endDateDay, int endDateYear, int durationHour,
642             int durationMinute, boolean allDay, boolean timeZoneSensitive,
643             String type, boolean repeating, Recurrence recurrence,
644             String remindBy, int firstReminder, int secondReminder)
645         throws PortalException, SystemException {
646 
647         User user = userPersistence.findByPrimaryKey(userId);
648 
649         Locale locale = null;
650         TimeZone timeZone = null;
651 
652         if (timeZoneSensitive) {
653             locale = user.getLocale();
654             timeZone = user.getTimeZone();
655         }
656         else {
657             locale = LocaleUtil.getDefault();
658             timeZone = TimeZoneUtil.getDefault();
659         }
660 
661         Calendar startDate = CalendarFactoryUtil.getCalendar(timeZone, locale);
662 
663         startDate.set(Calendar.MONTH, startDateMonth);
664         startDate.set(Calendar.DATE, startDateDay);
665         startDate.set(Calendar.YEAR, startDateYear);
666         startDate.set(Calendar.HOUR_OF_DAY, startDateHour);
667         startDate.set(Calendar.MINUTE, startDateMinute);
668         startDate.set(Calendar.SECOND, 0);
669         startDate.set(Calendar.MILLISECOND, 0);
670 
671         Calendar endDate = CalendarFactoryUtil.getCalendar(timeZone, locale);
672 
673         endDate.set(Calendar.MONTH, endDateMonth);
674         endDate.set(Calendar.DATE, endDateDay);
675         endDate.set(Calendar.YEAR, endDateYear);
676         endDate.set(Calendar.HOUR_OF_DAY, 23);
677         endDate.set(Calendar.MINUTE, 59);
678         endDate.set(Calendar.SECOND, 59);
679         endDate.set(Calendar.MILLISECOND, 999);
680 
681         if (allDay) {
682             startDate.set(Calendar.HOUR_OF_DAY, 0);
683             startDate.set(Calendar.MINUTE, 0);
684 
685             durationHour = 24;
686             durationMinute = 0;
687         }
688 
689         validate(
690             title, startDateMonth, startDateDay, startDateYear, endDateMonth,
691             endDateDay, endDateYear, durationHour, durationMinute, allDay);
692 
693         CalEvent event = calEventPersistence.findByPrimaryKey(eventId);
694 
695         event.setModifiedDate(new Date());
696         event.setTitle(title);
697         event.setDescription(description);
698         event.setStartDate(startDate.getTime());
699         event.setEndDate(endDate.getTime());
700         event.setDurationHour(durationHour);
701         event.setDurationMinute(durationMinute);
702         event.setAllDay(allDay);
703         event.setTimeZoneSensitive(timeZoneSensitive);
704         event.setType(type);
705         event.setRepeating(repeating);
706         event.setRecurrence(Base64.objectToString(recurrence));
707         event.setRemindBy(remindBy);
708         event.setFirstReminder(firstReminder);
709         event.setSecondReminder(secondReminder);
710 
711         calEventPersistence.update(event, false);
712 
713         CalEventLocalUtil.clearEventsPool(event.getGroupId());
714 
715         return event;
716     }
717 
718     protected File exportICal4j(
719             net.fortuna.ical4j.model.Calendar cal, String fileName)
720         throws SystemException {
721 
722         OutputStream os = null;
723 
724         try {
725             String extension = ".ics";
726 
727             if (Validator.isNull(fileName)) {
728                 fileName = "liferay.";
729             }
730             else {
731                 int pos = fileName.lastIndexOf(StringPool.PERIOD);
732 
733                 if (pos != -1) {
734                     extension = fileName.substring(pos);
735                     fileName = fileName.substring(0, pos);
736                 }
737             }
738 
739             File file = File.createTempFile(fileName, extension);
740 
741             os = new BufferedOutputStream(new FileOutputStream(file.getPath()));
742 
743             CalendarOutputter calOutput = new CalendarOutputter();
744 
745             calOutput.output(cal, os);
746 
747             return file;
748         }
749         catch (Exception e) {
750             _log.error(e, e);
751 
752             throw new SystemException(e);
753         }
754         finally {
755             ServletResponseUtil.cleanUp(os);
756         }
757     }
758 
759     protected Calendar getRecurrenceCal(
760         Calendar cal, Calendar tzICal, CalEvent event) {
761 
762         Calendar eventCal = CalendarFactoryUtil.getCalendar();
763         eventCal.setTime(event.getStartDate());
764 
765         Calendar recurrenceCal = (Calendar)tzICal.clone();
766         recurrenceCal.set(
767             Calendar.HOUR_OF_DAY, eventCal.get(Calendar.HOUR_OF_DAY));
768         recurrenceCal.set(
769             Calendar.MINUTE, eventCal.get(Calendar.MINUTE));
770         recurrenceCal.set(Calendar.SECOND, 0);
771         recurrenceCal.set(Calendar.MILLISECOND, 0);
772 
773         if (event.isTimeZoneSensitive()) {
774             int gmtDate = eventCal.get(Calendar.DATE);
775             long gmtMills = eventCal.getTimeInMillis();
776 
777             eventCal.setTimeZone(cal.getTimeZone());
778 
779             int tziDate = eventCal.get(Calendar.DATE);
780             long tziMills = Time.getDate(eventCal).getTime();
781 
782             if (gmtDate != tziDate) {
783                 int diffDate = 0;
784 
785                 if (gmtMills > tziMills) {
786                     diffDate = (int)Math.ceil(
787                         (double)(gmtMills - tziMills) / Time.DAY);
788                 }
789                 else {
790                     diffDate = (int)Math.floor(
791                         (double)(gmtMills - tziMills) / Time.DAY);
792                 }
793 
794                 recurrenceCal.add(Calendar.DATE, diffDate);
795             }
796         }
797 
798         return recurrenceCal;
799     }
800 
801     protected void importICal4j(
802             long userId, long plid, VEvent event, TimeZone timeZone)
803         throws PortalException, SystemException {
804 
805         String title = StringPool.BLANK;
806 
807         if (event.getSummary() != null) {
808             title = event.getSummary().getValue();
809         }
810 
811         String description = StringPool.BLANK;
812 
813         if (event.getDescription() != null) {
814             description = event.getDescription().getValue();
815         }
816 
817         Calendar startDate = Calendar.getInstance(timeZone);
818 
819         startDate.setTime(event.getStartDate().getDate());
820 
821         Calendar endDate = Calendar.getInstance(timeZone);
822 
823         endDate.setTime(event.getEndDate().getDate());
824 
825         long diffMillis =
826             endDate.getTimeInMillis() - startDate.getTimeInMillis();
827         long durationHours = diffMillis / (60 * 60 * 1000);
828         long durationMins =
829             (diffMillis / (60 * 1000)) - (durationHours * 60);
830         boolean allDay = false;
831 
832         if (event.getProperty(Property.DTSTART).getParameter(
833                 Parameter.TZID) == null){
834 
835             Calendar startDateWithoutTimeZone = Calendar.getInstance();
836 
837             startDateWithoutTimeZone.setTime(
838                 event.getStartDate().getDate());
839 
840             startDate.set(
841                 Calendar.DAY_OF_MONTH,
842                 startDateWithoutTimeZone.get(Calendar.DAY_OF_MONTH));
843             startDate.set(Calendar.HOUR_OF_DAY, 0);
844             startDate.set(Calendar.MINUTE, 0);
845             startDate.set(Calendar.SECOND, 0);
846             startDate.set(Calendar.MILLISECOND, 0);
847 
848             durationHours = 24;
849             durationMins = 0;
850             allDay = true;
851         }
852 
853         boolean timeZoneSensitive = false;
854         String type = StringPool.BLANK;
855         boolean repeating = false;
856         Recurrence recurrence = null;
857 
858         RRule rrule = (RRule)event.getProperty(Property.RRULE);
859 
860         if (rrule != null) {
861             repeating = true;
862             recurrence = toRecurrence(
863                 rrule.getRecur(), timeZone, startDate);
864 
865             if (recurrence.getUntil() != null) {
866                 endDate = recurrence.getUntil();
867             }
868         }
869 
870         String remindBy = "none";
871         int firstReminder = 300000;
872         int secondReminder = 300000;
873 
874         boolean addCommunityPermissions = false;
875         boolean addGuestPermissions = false;
876 
877         addEvent(
878             userId, plid, title, description, startDate.get(Calendar.MONTH),
879             startDate.get(Calendar.DAY_OF_MONTH),
880             startDate.get(Calendar.YEAR),
881             startDate.get(Calendar.HOUR_OF_DAY),
882             startDate.get(Calendar.MINUTE), endDate.get(Calendar.MONTH),
883             endDate.get(Calendar.DAY_OF_MONTH), endDate.get(Calendar.YEAR),
884             (int)durationHours, (int)durationMins, allDay,
885             timeZoneSensitive, type, repeating, recurrence, remindBy,
886             firstReminder, secondReminder, addCommunityPermissions,
887             addGuestPermissions);
888 
889     }
890 
891     protected void remindUser(CalEvent event, User user) {
892         String remindBy = event.getRemindBy();
893 
894         if (remindBy.equals(CalEventImpl.REMIND_BY_NONE)) {
895             return;
896         }
897 
898         try {
899             long ownerId = event.getGroupId();
900             int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
901             long plid = PortletKeys.PREFS_PLID_SHARED;
902             String portletId = PortletKeys.CALENDAR;
903 
904             PortletPreferences prefs =
905                 portletPreferencesLocalService.getPreferences(
906                     event.getCompanyId(), ownerId, ownerType, plid, portletId);
907 
908             Company company = companyPersistence.findByPrimaryKey(
909                 user.getCompanyId());
910 
911             Contact contact = user.getContact();
912 
913             String portletName = PortalUtil.getPortletTitle(
914                 PortletKeys.CALENDAR, user);
915 
916             String fromName = CalUtil.getEmailFromName(prefs);
917             String fromAddress = CalUtil.getEmailFromAddress(prefs);
918 
919             String toName = user.getFullName();
920             String toAddress = user.getEmailAddress();
921 
922             if (remindBy.equals(CalEventImpl.REMIND_BY_SMS)) {
923                 toAddress = contact.getSmsSn();
924             }
925 
926             String subject = CalUtil.getEmailEventReminderSubject(prefs);
927             String body = CalUtil.getEmailEventReminderBody(prefs);
928 
929             DateFormat dateFormatDateTime = DateFormats.getDateTime(
930                 user.getLocale(), user.getTimeZone());
931 
932             subject = StringUtil.replace(
933                 subject,
934                 new String[] {
935                     "[$EVENT_START_DATE$]",
936                     "[$EVENT_TITLE$]",
937                     "[$FROM_ADDRESS$]",
938                     "[$FROM_NAME$]",
939                     "[$PORTAL_URL$]",
940                     "[$PORTLET_NAME$]",
941                     "[$TO_ADDRESS$]",
942                     "[$TO_NAME$]"
943                 },
944                 new String[] {
945                     dateFormatDateTime.format(event.getStartDate()),
946                     event.getTitle(),
947                     fromAddress,
948                     fromName,
949                     company.getVirtualHost(),
950                     portletName,
951                     toAddress,
952                     toName,
953                 });
954 
955             body = StringUtil.replace(
956                 body,
957                 new String[] {
958                     "[$EVENT_START_DATE$]",
959                     "[$EVENT_TITLE$]",
960                     "[$FROM_ADDRESS$]",
961                     "[$FROM_NAME$]",
962                     "[$PORTAL_URL$]",
963                     "[$PORTLET_NAME$]",
964                     "[$TO_ADDRESS$]",
965                     "[$TO_NAME$]"
966                 },
967                 new String[] {
968                     dateFormatDateTime.format(event.getStartDate()),
969                     event.getTitle(),
970                     fromAddress,
971                     fromName,
972                     company.getVirtualHost(),
973                     portletName,
974                     toAddress,
975                     toName,
976                 });
977 
978             if (remindBy.equals(CalEventImpl.REMIND_BY_EMAIL) ||
979                 remindBy.equals(CalEventImpl.REMIND_BY_SMS)) {
980 
981                 InternetAddress from = new InternetAddress(
982                     fromAddress, fromName);
983 
984                 InternetAddress to = new InternetAddress(toAddress, toName);
985 
986                 MailMessage message = new MailMessage(
987                     from, to, subject, body, true);
988 
989                 mailService.sendEmail(message);
990             }
991             else if (remindBy.equals(CalEventImpl.REMIND_BY_AIM) &&
992                      Validator.isNotNull(contact.getAimSn())) {
993 
994                 AIMConnector.send(contact.getAimSn(), body);
995             }
996             else if (remindBy.equals(CalEventImpl.REMIND_BY_ICQ) &&
997                      Validator.isNotNull(contact.getIcqSn())) {
998 
999                 ICQConnector.send(contact.getIcqSn(), body);
1000            }
1001            else if (remindBy.equals(CalEventImpl.REMIND_BY_MSN) &&
1002                     Validator.isNotNull(contact.getMsnSn())) {
1003
1004                MSNConnector.send(contact.getMsnSn(), body);
1005            }
1006            else if (remindBy.equals(CalEventImpl.REMIND_BY_YM) &&
1007                     Validator.isNotNull(contact.getYmSn())) {
1008
1009                YMConnector.send(contact.getYmSn(), body);
1010            }
1011        }
1012        catch (Exception e) {
1013            _log.error(e);
1014        }
1015    }
1016
1017    protected int toCalendarWeekDay(WeekDay weekDay) {
1018        int dayOfWeeek = 0;
1019
1020        if (weekDay.getDay().equals(WeekDay.SU.getDay())) {
1021            dayOfWeeek = Calendar.SUNDAY;
1022        }
1023        else if (weekDay.getDay().equals(WeekDay.MO.getDay())) {
1024            dayOfWeeek = Calendar.MONDAY;
1025        }
1026        else if (weekDay.getDay().equals(WeekDay.TU.getDay())) {
1027            dayOfWeeek = Calendar.TUESDAY;
1028        }
1029        else if (weekDay.getDay().equals(WeekDay.WE.getDay())) {
1030            dayOfWeeek = Calendar.WEDNESDAY;
1031        }
1032        else if (weekDay.getDay().equals(WeekDay.TH.getDay())) {
1033            dayOfWeeek = Calendar.THURSDAY;
1034        }
1035        else if (weekDay.getDay().equals(WeekDay.FR.getDay())) {
1036            dayOfWeeek = Calendar.FRIDAY;
1037        }
1038        else if (weekDay.getDay().equals(WeekDay.SA.getDay())) {
1039            dayOfWeeek = Calendar.SATURDAY;
1040        }
1041
1042        return dayOfWeeek;
1043    }
1044
1045    protected net.fortuna.ical4j.model.Calendar toICalCalendar(
1046        long userId, List<CalEvent> events)
1047        throws PortalException, SystemException {
1048
1049        net.fortuna.ical4j.model.Calendar iCal =
1050            new net.fortuna.ical4j.model.Calendar();
1051
1052        ProdId prodId = new ProdId(
1053            "-//Liferay Inc//Liferay Portal " + ReleaseInfo.getVersion() +
1054            "//EN");
1055
1056        PropertyList props = iCal.getProperties();
1057
1058        props.add(prodId);
1059        props.add(Version.VERSION_2_0);
1060        props.add(CalScale.GREGORIAN);
1061
1062        User user = userPersistence.findByPrimaryKey(userId);
1063        TimeZone timeZone = user.getTimeZone();
1064
1065        List<VEvent> components = iCal.getComponents();
1066
1067        Iterator<CalEvent> itr = events.iterator();
1068
1069        while (itr.hasNext()) {
1070            CalEvent event = itr.next();
1071
1072            components.add(toICalVEvent(event, timeZone));
1073        }
1074
1075        return iCal;
1076    }
1077
1078    protected Recur toICalRecurrence(Recurrence recurrence) {
1079        Recur recur = null;
1080
1081        int recurrenceType = recurrence.getFrequency();
1082
1083        int interval = recurrence.getInterval();
1084
1085        if (recurrenceType == Recurrence.DAILY) {
1086            recur = new Recur(Recur.DAILY, -1);
1087
1088            if (interval >= 1) {
1089                recur.setInterval(interval);
1090            }
1091
1092            DayAndPosition[] byDay = recurrence.getByDay();
1093
1094            if (byDay != null) {
1095                for (int i = 0; i < byDay.length; i++) {
1096                    WeekDay weekDay = toICalWeekDay(byDay[i].getDayOfWeek());
1097
1098                    recur.getDayList().add(weekDay);
1099                }
1100            }
1101
1102        }
1103        else if (recurrenceType == Recurrence.WEEKLY) {
1104            recur = new Recur(Recur.WEEKLY, -1);
1105
1106            recur.setInterval(interval);
1107
1108            DayAndPosition[] byDay = recurrence.getByDay();
1109
1110            if (byDay != null) {
1111                for (int i = 0; i < byDay.length; i++) {
1112                    WeekDay weekDay = toICalWeekDay(byDay[i].getDayOfWeek());
1113
1114                    recur.getDayList().add(weekDay);
1115                }
1116            }
1117        }
1118        else if (recurrenceType == Recurrence.MONTHLY) {
1119            recur = new Recur(Recur.MONTHLY, -1);
1120
1121            recur.setInterval(interval);
1122
1123            int[] byMonthDay = recurrence.getByMonthDay();
1124
1125            if (byMonthDay != null) {
1126                Integer monthDay = new Integer(byMonthDay[0]);
1127
1128                recur.getMonthDayList().add(monthDay);
1129            }
1130            else if (recurrence.getByDay() != null){
1131                DayAndPosition[] byDay = recurrence.getByDay();
1132
1133                WeekDay weekDay = toICalWeekDay(byDay[0].getDayOfWeek());
1134
1135                recur.getDayList().add(weekDay);
1136
1137                Integer position = new Integer(byDay[0].getDayPosition());
1138
1139                recur.getSetPosList().add(position);
1140            }
1141        }
1142        else if (recurrenceType == Recurrence.YEARLY) {
1143            recur = new Recur(Recur.YEARLY, -1);
1144
1145            recur.setInterval(interval);
1146        }
1147
1148        Calendar until = recurrence.getUntil();
1149
1150        if (until != null) {
1151            DateTime dateTime = new DateTime(until.getTime());
1152
1153            recur.setUntil(dateTime);
1154        }
1155
1156        return recur;
1157    }
1158
1159    protected VEvent toICalVEvent(CalEvent event, TimeZone timeZone){
1160        VEvent vEvent = new VEvent();
1161
1162        PropertyList eventProps = vEvent.getProperties();
1163
1164        // UID
1165
1166        Uid uid = new Uid(PortalUUIDUtil.generate());
1167
1168        eventProps.add(uid);
1169
1170        DtStart dtStart = new DtStart(new DateTime(event.getStartDate()));
1171
1172        eventProps.add(dtStart);
1173
1174        // Duration
1175
1176        Calendar cal = Calendar.getInstance();
1177
1178        Date start = cal.getTime();
1179
1180        cal.add(Calendar.HOUR, event.getDurationHour());
1181        cal.add(Calendar.MINUTE, event.getDurationHour());
1182
1183        Date end = cal.getTime();
1184
1185        Duration duration = new Duration(start, end);
1186
1187        eventProps.add(duration);
1188
1189        // Summary
1190
1191        Summary summary = new Summary(event.getTitle());
1192
1193        eventProps.add(summary);
1194
1195        // Description
1196
1197        Description description = new Description(event.getDescription());
1198
1199        eventProps.add(description);
1200
1201        // Comment
1202
1203        Comment comment = new Comment(event.getType());
1204
1205        eventProps.add(comment);
1206
1207        // Recurrence rule
1208
1209        if (event.isRepeating()) {
1210            Recur recur = toICalRecurrence(event.getRecurrenceObj());
1211
1212            RRule rRule = new RRule(recur);
1213
1214            eventProps.add(rRule);
1215        }
1216
1217        return vEvent;
1218    }
1219
1220    protected WeekDay toICalWeekDay(int dayOfWeek) {
1221        WeekDay weekDay = null;
1222
1223        if (dayOfWeek == Calendar.SUNDAY) {
1224            weekDay = WeekDay.SU;
1225        }
1226        else if (dayOfWeek == Calendar.MONDAY) {
1227            weekDay = WeekDay.MO;
1228        }
1229        else if (dayOfWeek == Calendar.TUESDAY) {
1230            weekDay = WeekDay.TU;
1231        }
1232        else if (dayOfWeek == Calendar.WEDNESDAY) {
1233            weekDay = WeekDay.WE;
1234        }
1235        else if (dayOfWeek == Calendar.THURSDAY) {
1236            weekDay = WeekDay.TH;
1237        }
1238        else if (dayOfWeek == Calendar.FRIDAY) {
1239            weekDay = WeekDay.FR;
1240        }
1241        else if (dayOfWeek == Calendar.SATURDAY) {
1242            weekDay = WeekDay.SA;
1243        }
1244
1245        return weekDay;
1246    }
1247
1248    protected TimeZone toTimeZone(long userId, VTimeZone vTimeZone)
1249        throws SystemException, NoSuchUserException {
1250
1251        User user = userPersistence.findByPrimaryKey(userId);
1252
1253        TimeZone timeZone = user.getTimeZone();
1254
1255        timeZone = TimeZone.getTimeZone(
1256            vTimeZone.getTimeZoneId().getValue());
1257
1258        return timeZone;
1259    }
1260
1261    protected Recurrence toRecurrence(
1262        Recur recur, TimeZone timeZone, Calendar startDate) {
1263
1264        Calendar recStartCal = CalendarFactoryUtil.getCalendar(timeZone);
1265
1266        recStartCal.setTime(startDate.getTime());
1267
1268        Recurrence recurrence = new Recurrence(
1269            recStartCal,
1270            new com.liferay.portal.kernel.cal.Duration(1, 0, 0, 0));
1271
1272        recurrence.setWeekStart(Calendar.SUNDAY);
1273
1274        if (recur.getInterval() > 1){
1275            recurrence.setInterval(recur.getInterval());
1276        }
1277
1278        Calendar until = Calendar.getInstance(timeZone);
1279
1280        if (recur.getUntil() != null) {
1281            until.setTime(recur.getUntil());
1282
1283            recurrence.setUntil(until);
1284        }
1285
1286        String frequency = recur.getFrequency();
1287
1288        if (Recur.DAILY.equals(frequency)) {
1289            recurrence.setFrequency(Recurrence.DAILY);
1290
1291            List<DayAndPosition> dayPosList = new ArrayList<DayAndPosition>();
1292
1293            Iterator<WeekDay> itr = recur.getDayList().iterator();
1294
1295            while (itr.hasNext()) {
1296                WeekDay weekDay = itr.next();
1297
1298                dayPosList.add(
1299                    new DayAndPosition(toCalendarWeekDay(weekDay), 0));
1300            }
1301
1302            if (!dayPosList.isEmpty()) {
1303                recurrence.setByDay(
1304                    dayPosList.toArray(new DayAndPosition[dayPosList.size()]));
1305            }
1306        }
1307        else if (Recur.WEEKLY.equals(frequency)) {
1308            recurrence.setFrequency(Recurrence.WEEKLY);
1309
1310            List<DayAndPosition> dayPosList = new ArrayList<DayAndPosition>();
1311
1312            Iterator<WeekDay> itr = recur.getDayList().iterator();
1313
1314            while (itr.hasNext()) {
1315                WeekDay weekDay = itr.next();
1316
1317                dayPosList.add(
1318                    new DayAndPosition(toCalendarWeekDay(weekDay), 0));
1319            }
1320
1321            if (!dayPosList.isEmpty()) {
1322                recurrence.setByDay(
1323                    dayPosList.toArray(new DayAndPosition[dayPosList.size()]));
1324            }
1325        }
1326        else if (Recur.MONTHLY.equals(frequency)) {
1327            recurrence.setFrequency(Recurrence.MONTHLY);
1328
1329            Iterator<Integer> monthDayListItr =
1330                recur.getMonthDayList().iterator();
1331
1332            if (monthDayListItr.hasNext()) {
1333                Integer monthDay = monthDayListItr.next();
1334
1335                recurrence.setByMonthDay(new int[] {monthDay.intValue()});
1336            }
1337
1338            Iterator<WeekDay> dayListItr = recur.getDayList().iterator();
1339
1340            if (dayListItr.hasNext()) {
1341                WeekDay weekDay = dayListItr.next();
1342
1343                DayAndPosition[] dayPos = {
1344                    new DayAndPosition(toCalendarWeekDay(weekDay),
1345                    weekDay.getOffset())
1346                };
1347
1348                recurrence.setByDay(dayPos);
1349            }
1350        }
1351        else if (Recur.YEARLY.equals(frequency)) {
1352            recurrence.setFrequency(Recurrence.YEARLY);
1353        }
1354
1355        return recurrence;
1356    }
1357
1358    protected void validate(
1359            String title, int startDateMonth, int startDateDay,
1360            int startDateYear, int endDateMonth, int endDateDay,
1361            int endDateYear, int durationHour, int durationMinute,
1362            boolean allDay)
1363        throws PortalException, SystemException {
1364
1365        if (Validator.isNull(title)) {
1366            throw new EventTitleException();
1367        }
1368        else if (!Validator.isDate(
1369                startDateMonth, startDateDay, startDateYear)) {
1370
1371            throw new EventStartDateException();
1372        }
1373        else if (!Validator.isDate(endDateMonth, endDateDay, endDateYear)) {
1374            throw new EventEndDateException();
1375        }
1376
1377        if (!allDay && durationHour <= 0 && durationMinute <= 0) {
1378            throw new EventDurationException();
1379        }
1380
1381        Calendar startDate = CalendarFactoryUtil.getCalendar(
1382            startDateYear, startDateMonth, startDateDay);
1383
1384        Calendar endDate = CalendarFactoryUtil.getCalendar(
1385            endDateYear, endDateMonth, endDateDay);
1386
1387        if (startDate.after(endDate)) {
1388            throw new EventEndDateException();
1389        }
1390    }
1391
1392    private static Log _log = LogFactory.getLog(CalEventLocalServiceImpl.class);
1393
1394}