/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <p>
*/
package org.olat.commons.calendar;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.CalendarOutputter;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.DateList;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Parameter;
import net.fortuna.ical4j.model.Property;
import net.fortuna.ical4j.model.PropertyList;
import net.fortuna.ical4j.model.Recur;
import net.fortuna.ical4j.model.TimeZone;
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.component.VTimeZone;
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.model.property.CalScale;
import net.fortuna.ical4j.model.property.Clazz;
import net.fortuna.ical4j.model.property.Created;
import net.fortuna.ical4j.model.property.Duration;
import net.fortuna.ical4j.model.property.ExDate;
import net.fortuna.ical4j.model.property.LastModified;
import net.fortuna.ical4j.model.property.Location;
import net.fortuna.ical4j.model.property.ProdId;
import net.fortuna.ical4j.model.property.RRule;
import net.fortuna.ical4j.model.property.Uid;
import net.fortuna.ical4j.model.property.Version;
import net.fortuna.ical4j.model.property.XProperty;
import org.olat.admin.user.delete.service.UserDeletionManager;
import org.olat.commons.calendar.model.Kalendar;
import org.olat.commons.calendar.model.KalendarConfig;
import org.olat.commons.calendar.model.KalendarEvent;
import org.olat.commons.calendar.model.KalendarEventLink;
import org.olat.commons.calendar.model.KalendarRecurEvent;
import org.olat.commons.calendar.ui.components.KalendarRenderWrapper;
import org.olat.commons.calendar.ui.events.KalendarModifiedEvent;
import org.olat.core.gui.UserRequest;
import org.olat.core.id.Identity;
import org.olat.core.id.OLATResourceable;
import org.olat.core.logging.OLATRuntimeException;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.FileUtils;
import org.olat.core.util.cache.n.CacheWrapper;
import org.olat.core.util.coordinate.CoordinatorManager;
import org.olat.core.util.coordinate.SyncerCallback;
import org.olat.core.util.prefs.Preferences;
import org.olat.core.util.resource.OresHelper;
import org.olat.course.ICourse;
import org.olat.group.BusinessGroup;
public class ICalFileCalendarManager implements CalendarManager {
private static final OLog log = Tracing.createLoggerFor(ICalFileCalendarManager.class);
private File fStorageBase;
// o_clusterOK by:cg
private CacheWrapper calendarCache;
private static final Clazz ICAL_CLASS_PRIVATE = new Clazz("PRIVATE");
private static final Clazz ICAL_CLASS_PUBLIC = new Clazz("PUBLIC");
private static final Clazz ICAL_CLASS_X_FREEBUSY = new Clazz("X-FREEBUSY");
private static final String ICAL_X_OLAT_LINK = "X-OLAT-LINK";
private static final String ICAL_X_OLAT_COMMENT = "X-OLAT-COMMENT";
private static final String ICAL_X_OLAT_NUMPARTICIPANTS = "X-OLAT-NUMPARTICIPANTS";
private static final String ICAL_X_OLAT_PARTICIPANTS = "X-OLAT-PARTICIPANTS";
private static final String ICAL_X_OLAT_SOURCENODEID = "X-OLAT-SOURCENODEID";
/** rule for recurring events */
private static final String ICAL_RRULE = "RRULE";
/** property to exclude events from recurrence */
private static final String ICAL_EXDATE = "EXDATE";
private TimeZone tz;
public ICalFileCalendarManager(File fStorageBase) {
this.fStorageBase = fStorageBase;
if (!fStorageBase.exists()) {
if (!fStorageBase.mkdirs())
throw new OLATRuntimeException("Error creating calendar base directory at: " + fStorageBase.getAbsolutePath(), null);
}
createCalendarFileDirectories();
// set parser to relax (needed for allday events
// see http://sourceforge.net/forum/forum.php?thread_id=1253735&forum_id=368291
System.setProperty("ical4j.unfolding.relaxed", "true");
// initialize tiemzone
tz = TimeZoneRegistryFactory.getInstance().createRegistry().getTimeZone(java.util.Calendar.getInstance().getTimeZone().getID());
calendarCache = CoordinatorManager.getCoordinator().getCacher().getOrCreateCache(this.getClass(), "calendar");
UserDeletionManager.getInstance().registerDeletableUserData(this);
}
/**
* Check if a calendar already exists for the given id.
* @param calendarID
* @param type
* @return
*/
public boolean calendarExists(String calendarType, String calendarID) {
return getCalendarFile(calendarType, calendarID).exists();
}
/**
*
* @see org.olat.calendar.CalendarManager#createClaendar(java.lang.String)
*/
public Kalendar createCalendar(String type, String calendarID) {
return new Kalendar(calendarID, type);
}
public Kalendar getCalendar(String type, String calendarID) {
//o_clusterOK by:cg
OLATResourceable calOres = OresHelper.createOLATResourceableType(getKeyFor(type,calendarID));
final String callType = type;
final String callCalendarID = calendarID;
Kalendar cal = CoordinatorManager.getCoordinator().getSyncer().doInSync( calOres, new SyncerCallback<Kalendar>() {
public Kalendar execute() {
return getCalendarFromCache(callType, callCalendarID);
}
});
return cal;
}
protected Kalendar getCalendarFromCache(final String callType, final String callCalendarID) {
OLATResourceable calOres = OresHelper.createOLATResourceableType(getKeyFor(callType,callCalendarID));
CoordinatorManager.getCoordinator().getSyncer().assertAlreadyDoInSyncFor(calOres);
String key = getKeyFor(callType,callCalendarID);
Kalendar cal = (Kalendar)calendarCache.get(key);
if (cal == null) {
cal = loadOrCreateCalendar(callType, callCalendarID);
calendarCache.put(key, cal);
}
return cal;
}
/**
* {@inheritDoc}
*/
public OLATResourceable getOresHelperFor(Kalendar cal) {
return OresHelper.createOLATResourceableType(getKeyFor(cal.getType(), cal.getCalendarID()));
}
private String getKeyFor(String type, String calendarID) {
return type + "_" + calendarID;
}
/**
* Internal load calendar file from filesystem.
*/
// o_clusterOK by:cg This must not be synchronized because the caller already synchronized
private Kalendar loadCalendarFromFile(String type, String calendarID) {
Calendar calendar = readCalendar(type, calendarID);
Kalendar kalendar = createKalendar(type, calendarID, calendar);
return kalendar;
}
private Kalendar createKalendar(String type, String calendarID, Calendar calendar) {
Kalendar cal = new Kalendar(calendarID, type);
for (Iterator iter = calendar.getComponents().iterator(); iter.hasNext();) {
Component comp = (Component) iter.next();
if (comp instanceof VEvent) {
VEvent vevent = (VEvent)comp;
KalendarEvent calEvent = getKalendarEvent(vevent);
cal.addEvent(calEvent);
} else if (comp instanceof VTimeZone) {
log.info("createKalendar: VTimeZone Component is not supported and will not be added to calender");
log.debug("createKalendar: VTimeZone=" + comp);
} else {
log.warn("createKalendar: unknown Component=" + comp);
}
}
return cal;
}
/**
* Internal read calendar file from filesystem
*/
public Calendar readCalendar(String type, String calendarID) {
log.debug("readCalendar from file, type=" + type + " calendarID=" + calendarID);
File calendarFile = getCalendarFile(type, calendarID);
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(calendarFile));
} catch (FileNotFoundException fne) {
throw new OLATRuntimeException("Not found: " + calendarFile, fne);
}
CalendarBuilder builder = new CalendarBuilder();
Calendar calendar = null;
try {
calendar = builder.build(in);
} catch (Exception e) {
throw new OLATRuntimeException("Error parsing calendar file.", e);
} finally {
if (in != null)
FileUtils.closeSafely(in);
}
return calendar;
}
public Kalendar buildKalendarFrom(String calendarContent, String calType, String calId) {
Kalendar kalendar = null;
BufferedReader reader = new BufferedReader(new StringReader(calendarContent));
CalendarBuilder builder = new CalendarBuilder();
try {
Calendar calendar = builder.build(reader);
kalendar = createKalendar(calType, calId, calendar);
} catch (Exception e) {
throw new OLATRuntimeException("Error parsing calendar file.", e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
throw new OLATRuntimeException("Could not close reader after build calendar file.", e);
}
}
}
return kalendar;
}
/**
* Save a calendar.
* This method is not thread-safe. Must be called from a synchronized block.
* Be sure to have the newest calendar (reload calendar in synchronized block before safe it).
* @param calendar
*/
// o_clusterOK by:cg only called by Junit-test
public boolean persistCalendar(Kalendar kalendar) {
Calendar calendar = buildCalendar(kalendar);
boolean success = writeCalendarFile(calendar,kalendar.getType(), kalendar.getCalendarID());
calendarCache.update(getKeyFor(kalendar.getType(), kalendar.getCalendarID()), kalendar);
return success;
}
private boolean writeCalendarFile(Calendar calendar, String calType, String calId) {
File fKalendarFile = getCalendarFile(calType, calId);
OutputStream os = null;
try {
os = new BufferedOutputStream(new FileOutputStream(fKalendarFile, false));
CalendarOutputter calOut = new CalendarOutputter(false);
calOut.output(calendar, os);
} catch (Exception e) {
return false;
} finally {
FileUtils.closeSafely(os);
}
return true;
}
/**
* Delete calendar by type and id.
*/
public boolean deleteCalendar(String type, String calendarID) {
calendarCache.remove( getKeyFor(type,calendarID) );
File fKalendarFile = getCalendarFile(type, calendarID);
return fKalendarFile.delete();
}
public File getCalendarICalFile(String type, String calendarID) {
File fCalendarICalFile = getCalendarFile(type, calendarID);
if (fCalendarICalFile.exists()) return fCalendarICalFile;
else return null;
}
/**
* @see org.olat.calendar.CalendarManager#findKalendarConfigForIdentity(org.olat.calendar.model.Kalendar, org.olat.core.gui.UserRequest)
*/
public KalendarConfig findKalendarConfigForIdentity(Kalendar kalendar, UserRequest ureq) {
Preferences guiPreferences = ureq.getUserSession().getGuiPreferences();
return (KalendarConfig)guiPreferences.get(KalendarConfig.class, kalendar.getCalendarID());
}
/**
* @see org.olat.calendar.CalendarManager#saveKalendarConfigForIdentity(org.olat.calendar.model.KalendarConfig, org.olat.calendar.model.Kalendar, org.olat.core.gui.UserRequest)
*/
public void saveKalendarConfigForIdentity(KalendarConfig config, Kalendar kalendar, UserRequest ureq) {
Preferences guiPreferences = ureq.getUserSession().getGuiPreferences();
guiPreferences.putAndSave(KalendarConfig.class, kalendar.getCalendarID(), config);
}
private Calendar buildCalendar(Kalendar kalendar) {
Calendar calendar = new Calendar();
// add standard propeties
calendar.getProperties().add(new ProdId("-//Ben Fortuna//iCal4j 1.0//EN"));
calendar.getProperties().add(Version.VERSION_2_0);
calendar.getProperties().add(CalScale.GREGORIAN);
for (Iterator<KalendarEvent> iter = kalendar.getEvents().iterator(); iter.hasNext();) {
KalendarEvent kEvent = iter.next();
VEvent vEvent = getVEvent(kEvent);
calendar.getComponents().add(vEvent);
}
return calendar;
}
private VEvent getVEvent(KalendarEvent kEvent) {
VEvent vEvent = new VEvent();
if (!kEvent.isAllDayEvent()) {
// regular VEvent
DateTime dtBegin = new DateTime(kEvent.getBegin());
dtBegin.setTimeZone(tz);
DateTime dtEnd = new DateTime(kEvent.getEnd());
dtEnd.setTimeZone(tz);
vEvent = new VEvent(dtBegin, dtEnd, kEvent.getSubject());
} else {
// AllDay VEvent
net.fortuna.ical4j.model.Date dtBegin = new net.fortuna.ical4j.model.Date(kEvent.getBegin());
// adjust end date: ICal end dates for all day events are on the next day
Date adjustedEndDate = new Date(kEvent.getEnd().getTime() + (1000 * 60 * 60 * 24));
net.fortuna.ical4j.model.Date dtEnd = new net.fortuna.ical4j.model.Date(adjustedEndDate);
vEvent = new VEvent(dtBegin, dtEnd, kEvent.getSubject());
vEvent.getProperties().getProperty(Property.DTSTART).getParameters().add(Value.DATE);
vEvent.getProperties().getProperty(Property.DTEND).getParameters().add(Value.DATE);
}
if(kEvent.getCreated() > 0) {
Created created = new Created(new DateTime(kEvent.getCreated()));
vEvent.getProperties().add(created);
}
if(kEvent.getLastModified() > 0) {
LastModified lastMod = new LastModified(new DateTime(kEvent.getLastModified()));
vEvent.getProperties().add(lastMod);
}
// Uid
PropertyList vEventProperties = vEvent.getProperties();
vEventProperties.add(new Uid(kEvent.getID()));
// clazz
switch (kEvent.getClassification()) {
case KalendarEvent.CLASS_PRIVATE: vEventProperties.add(ICAL_CLASS_PRIVATE); break;
case KalendarEvent.CLASS_PUBLIC: vEventProperties.add(ICAL_CLASS_PUBLIC); break;
case KalendarEvent.CLASS_X_FREEBUSY: vEventProperties.add(ICAL_CLASS_X_FREEBUSY); break;
default: vEventProperties.add(ICAL_CLASS_PRIVATE); break;
}
// location
if (kEvent.getLocation() != null) {
vEventProperties.add(new Location(kEvent.getLocation()));
}
// event links
List kalendarEventLinks = kEvent.getKalendarEventLinks();
if ((kalendarEventLinks != null) && !kalendarEventLinks.isEmpty()) {
for (Iterator iter = kalendarEventLinks.iterator(); iter.hasNext();) {
KalendarEventLink link = (KalendarEventLink) iter.next();
StringBuilder linkEncoded = new StringBuilder(200);
linkEncoded.append(link.getProvider());
linkEncoded.append("§");
linkEncoded.append(link.getId());
linkEncoded.append("§");
linkEncoded.append(link.getDisplayName());
linkEncoded.append("§");
linkEncoded.append(link.getURI());
linkEncoded.append("§");
linkEncoded.append(link.getIconCssClass());
XProperty linkProperty = new XProperty(ICAL_X_OLAT_LINK, linkEncoded.toString());
vEventProperties.add(linkProperty);
}
}
if (kEvent.getComment() != null) {
vEventProperties.add(new XProperty(ICAL_X_OLAT_COMMENT, kEvent.getComment()));
}
if (kEvent.getNumParticipants() != null) {
vEventProperties.add(new XProperty(ICAL_X_OLAT_NUMPARTICIPANTS, Integer.toString(kEvent.getNumParticipants())));
}
if (kEvent.getParticipants() != null) {
StringBuffer strBuf = new StringBuffer();
String[] participants = kEvent.getParticipants();
for ( String participant : participants ) {
strBuf.append(participant);
strBuf.append("§");
}
vEventProperties.add(new XProperty(ICAL_X_OLAT_PARTICIPANTS, strBuf.toString()));
}
if (kEvent.getSourceNodeId() != null) {
vEventProperties.add(new XProperty(ICAL_X_OLAT_SOURCENODEID, kEvent.getSourceNodeId()));
}
// recurrence
String recurrence = kEvent.getRecurrenceRule();
if(recurrence != null && !recurrence.equals("")) {
try {
Recur recur = new Recur(recurrence);
RRule rrule = new RRule(recur);
vEventProperties.add(rrule);
} catch (ParseException e) {
Tracing.createLoggerFor(getClass()).error("cannot create recurrence rule: " + recurrence.toString(), e);
}
}
// recurrence exclusions
String recurrenceExc = kEvent.getRecurrenceExc();
if(recurrenceExc != null && !recurrenceExc.equals("")) {
ExDate exdate = new ExDate();
try {
exdate.setValue(recurrenceExc);
vEventProperties.add(exdate);
} catch (ParseException e) {
e.printStackTrace();
}
}
return vEvent;
}
/**
* Build a KalendarEvent out of a source VEvent.
* @param event
* @return
*/
private KalendarEvent getKalendarEvent(VEvent event) {
// subject
String subject = event.getSummary().getValue();
// start
Date start = event.getStartDate().getDate();
Duration dur = event.getDuration();
// end
Date end = null;
if (dur != null) {
end = dur.getDuration().getTime(event.getStartDate().getDate());
} else {
end = event.getEndDate().getDate();
}
// check all day event first
boolean isAllDay = false;
Parameter dateParameter = event.getProperties().getProperty(Property.DTSTART).getParameters().getParameter(Value.DATE.getName());
if (dateParameter != null) isAllDay = true;
if (isAllDay) {
// adjust end date: ICal sets end dates to the next day
end = new Date(end.getTime() - (1000 * 60 * 60 * 24));
}
KalendarEvent calEvent = new KalendarEvent(event.getUid().getValue(), subject, start, end);
calEvent.setAllDayEvent(isAllDay);
// classification
Clazz classification = event.getClassification();
if (classification != null) {
String sClass = classification.getValue();
int iClassification = KalendarEvent.CLASS_PRIVATE;
if (sClass.equals(ICAL_CLASS_PRIVATE.getValue())) iClassification = KalendarEvent.CLASS_PRIVATE;
else if (sClass.equals(ICAL_CLASS_X_FREEBUSY.getValue())) iClassification = KalendarEvent.CLASS_X_FREEBUSY;
else if (sClass.equals(ICAL_CLASS_PUBLIC.getValue())) iClassification = KalendarEvent.CLASS_PUBLIC;
calEvent.setClassification(iClassification);
}
// created/last modified
Created created = event.getCreated();
if (created != null) {
calEvent.setCreated(created.getDate().getTime());
}
LastModified lastModified = event.getLastModified();
if (lastModified != null) {
calEvent.setLastModified(lastModified.getDate().getTime());
}
// location
Location location = event.getLocation();
if (location != null) {
calEvent.setLocation(location.getValue());
}
// links if any
List linkProperties = event.getProperties(ICAL_X_OLAT_LINK);
List kalendarEventLinks = new ArrayList();
for (Iterator iter = linkProperties.iterator(); iter.hasNext();) {
XProperty linkProperty = (XProperty) iter.next();
if (linkProperty != null) {
String encodedLink = linkProperty.getValue();
StringTokenizer st = new StringTokenizer(encodedLink, "§", false);
if (st.countTokens() >= 4) {
String provider = st.nextToken();
String id = st.nextToken();
String displayName = st.nextToken();
String uri = st.nextToken();
String iconCss = "";
// migration: iconCss has been added later, check if available first
if (st.hasMoreElements()) {
iconCss = st.nextToken();
}
KalendarEventLink eventLink = new KalendarEventLink(provider, id, displayName, uri, iconCss);
kalendarEventLinks.add(eventLink);
}
}
}
calEvent.setKalendarEventLinks(kalendarEventLinks);
Property comment = event.getProperty(ICAL_X_OLAT_COMMENT);
if (comment != null)
calEvent.setComment(comment.getValue());
Property numParticipants = event.getProperty(ICAL_X_OLAT_NUMPARTICIPANTS);
if (numParticipants != null)
calEvent.setNumParticipants(Integer.parseInt(numParticipants.getValue()));
Property participants = event.getProperty(ICAL_X_OLAT_PARTICIPANTS);
if (participants != null) {
StringTokenizer strTok = new StringTokenizer(participants.getValue(), "§", false);
String[] parts = new String[strTok.countTokens()];
for ( int i = 0; strTok.hasMoreTokens(); i++ ) {
parts[i] = strTok.nextToken();
}
calEvent.setParticipants(parts);
}
Property sourceNodId = event.getProperty(ICAL_X_OLAT_SOURCENODEID);
if (sourceNodId != null)
calEvent.setSourceNodeId(sourceNodId.getValue());
// recurrence
if (event.getProperty(ICAL_RRULE) != null) {
calEvent.setRecurrenceRule(event.getProperty(ICAL_RRULE).getValue());
}
// recurrence exclusions
if (event.getProperty(ICAL_EXDATE) != null) {
calEvent.setRecurrenceExc(event.getProperty(ICAL_EXDATE).getValue());
}
return calEvent;
}
/**
* {@inheritDoc}
*/
public KalendarEvent getRecurringInPeriod(Date periodStart, Date periodEnd, KalendarEvent kEvent) {
boolean isRecurring= isRecurringInPeriod(periodStart, periodEnd, kEvent);
KalendarEvent recurEvent = null;
if(isRecurring) {
java.util.Calendar periodStartCal = java.util.Calendar.getInstance();
java.util.Calendar eventBeginCal = java.util.Calendar.getInstance();
periodStartCal.setTime(periodStart);
eventBeginCal.setTime(kEvent.getBegin());
Long duration = kEvent.getEnd().getTime() - kEvent.getBegin().getTime();
java.util.Calendar beginCal = java.util.Calendar.getInstance();
beginCal.setTime(kEvent.getBegin());
beginCal.set(java.util.Calendar.YEAR, periodStartCal.get(java.util.Calendar.YEAR));
beginCal.set(java.util.Calendar.MONTH, periodStartCal.get(java.util.Calendar.MONTH));
beginCal.set(java.util.Calendar.DAY_OF_MONTH, periodStartCal.get(java.util.Calendar.DAY_OF_MONTH));
recurEvent = kEvent.clone();
recurEvent.setBegin(beginCal.getTime());
recurEvent.setEnd(new Date(beginCal.getTime().getTime() + duration));
}
return recurEvent;
}
/**
* {@inheritDoc}
*/
public boolean isRecurringInPeriod(Date periodStart, Date periodEnd, KalendarEvent kEvent) {
DateList recurDates = getRecurringsInPeriod(periodStart, periodEnd, kEvent);
return (recurDates != null && !recurDates.isEmpty());
}
private DateList getRecurringsInPeriod(Date periodStart, Date periodEnd, KalendarEvent kEvent) {
DateList recurDates = null;
String recurrenceRule = kEvent.getRecurrenceRule();
if(recurrenceRule != null && !recurrenceRule.equals("")) {
try {
Recur recur = new Recur(recurrenceRule);
net.fortuna.ical4j.model.Date periodStartDate = new net.fortuna.ical4j.model.Date(periodStart);
net.fortuna.ical4j.model.Date periodEndDate = new net.fortuna.ical4j.model.Date(periodEnd);
net.fortuna.ical4j.model.Date eventStartDate = new net.fortuna.ical4j.model.Date(kEvent.getBegin());
recurDates = recur.getDates(eventStartDate, periodStartDate, periodEndDate, Value.DATE);
} catch (ParseException e) {
Tracing.createLoggerFor(getClass()).error("cannot restore recurrence rule: " + recurrenceRule, e);
}
String recurrenceExc = kEvent.getRecurrenceExc();
if(recurrenceExc != null && !recurrenceExc.equals("")) {
try {
ExDate exdate = new ExDate();
// expected date+time format:
// 20100730T100000
// unexpected all-day format:
// 20100730
// see OLAT-5645
if (recurrenceExc.length()>8) {
exdate.setValue(recurrenceExc);
} else {
exdate.getParameters().replace(Value.DATE);
exdate.setValue(recurrenceExc);
}
for( Object date : exdate.getDates() ) {
if(recurDates.contains(date)) recurDates.remove(date);
}
} catch (ParseException e) {
Tracing.createLoggerFor(getClass()).error("cannot restore excluded dates for this recurrence: " + recurrenceExc, e);
}
}
}
return recurDates;
}
/**
* @see org.olat.commons.calendar.CalendarManager#getRecurringDatesInPeriod(java.util.Date, java.util.Date, org.olat.commons.calendar.model.KalendarEvent)
*/
public List<KalendarRecurEvent> getRecurringDatesInPeriod(Date periodStart, Date periodEnd, KalendarEvent kEvent) {
List<KalendarRecurEvent> lstDates = new ArrayList<KalendarRecurEvent>();
DateList recurDates = getRecurringsInPeriod(periodStart, periodEnd, kEvent);
if(recurDates == null) return lstDates;
for( Object obj : recurDates ) {
net.fortuna.ical4j.model.Date date = (net.fortuna.ical4j.model.Date)obj;
KalendarRecurEvent recurEvent;
java.util.Calendar eventStartCal = java.util.Calendar.getInstance();
eventStartCal.clear();
eventStartCal.setTime(kEvent.getBegin());
java.util.Calendar eventEndCal = java.util.Calendar.getInstance();
eventEndCal.clear();
eventEndCal.setTime(kEvent.getEnd());
java.util.Calendar recurStartCal = java.util.Calendar.getInstance();
recurStartCal.clear();
recurStartCal.setTimeInMillis(date.getTime());
long duration = kEvent.getEnd().getTime() - kEvent.getBegin().getTime();
java.util.Calendar beginCal = java.util.Calendar.getInstance();
beginCal.clear();
beginCal.set(
recurStartCal.get(java.util.Calendar.YEAR),
recurStartCal.get(java.util.Calendar.MONTH),
recurStartCal.get(java.util.Calendar.DATE),
eventStartCal.get(java.util.Calendar.HOUR_OF_DAY),
eventStartCal.get(java.util.Calendar.MINUTE),
eventStartCal.get(java.util.Calendar.SECOND)
);
java.util.Calendar endCal = java.util.Calendar.getInstance();
endCal.clear();
endCal.setTimeInMillis(beginCal.getTimeInMillis() + duration);
if(kEvent.getBegin().compareTo(beginCal.getTime()) == 0) continue; //prevent doubled events
Date recurrenceEnd = CalendarUtils.getRecurrenceEndDate(kEvent.getRecurrenceRule());
if(kEvent.isAllDayEvent() && recurrenceEnd != null && recurStartCal.getTime().after(recurrenceEnd)) continue; //workaround for ical4j-bug in all day events
recurEvent = new KalendarRecurEvent(kEvent.getID(), kEvent.getSubject(), new Date(beginCal.getTimeInMillis()), new Date(endCal.getTimeInMillis()));
recurEvent.setSourceEvent(kEvent);
lstDates.add(recurEvent);
}
return lstDates;
}
public File getCalendarFile(String type, String calendarID) {
return new File(fStorageBase, "/" + type + "/" + calendarID + ".ics");
}
private void createCalendarFileDirectories() {
File fDirectory = new File(fStorageBase, "/" + TYPE_USER);
fDirectory.mkdirs();
fDirectory = new File(fStorageBase, "/" + TYPE_GROUP);
fDirectory.mkdirs();
fDirectory = new File(fStorageBase, "/" + TYPE_COURSE);
fDirectory.mkdirs();
}
public KalendarRenderWrapper getPersonalCalendar(Identity identity) {
Kalendar cal = getCalendar(CalendarManager.TYPE_USER, identity.getName());
KalendarRenderWrapper calendarWrapper = new KalendarRenderWrapper(cal);
KalendarConfig config = new KalendarConfig(identity.getName(), KalendarRenderWrapper.CALENDAR_COLOR_BLUE, true);
calendarWrapper.setKalendarConfig(config);
return calendarWrapper;
}
public KalendarRenderWrapper getImportedCalendar(Identity identity, String calendarName) {
Kalendar cal = getCalendar(CalendarManager.TYPE_USER, ImportCalendarManager.getImportedCalendarID(identity, calendarName));
KalendarRenderWrapper calendarWrapper = new KalendarRenderWrapper(cal);
KalendarConfig config = new KalendarConfig(calendarName, KalendarRenderWrapper.CALENDAR_COLOR_BLUE, true);
calendarWrapper.setKalendarConfig(config);
return calendarWrapper;
}
public KalendarRenderWrapper getGroupCalendar(BusinessGroup businessGroup) {
Kalendar cal = getCalendar(CalendarManager.TYPE_GROUP, businessGroup.getResourceableId().toString());
KalendarRenderWrapper calendarWrapper = new KalendarRenderWrapper(cal);
KalendarConfig config = new KalendarConfig(businessGroup.getName(), KalendarRenderWrapper.CALENDAR_COLOR_ORANGE, true);
calendarWrapper.setKalendarConfig(config);
return calendarWrapper;
}
public KalendarRenderWrapper getCourseCalendar(ICourse course) {
Kalendar cal = getCalendar(CalendarManager.TYPE_COURSE, course.getResourceableId().toString());
KalendarRenderWrapper calendarWrapper = new KalendarRenderWrapper(cal);
KalendarConfig config = new KalendarConfig(course.getCourseTitle(), KalendarRenderWrapper.CALENDAR_COLOR_GREEN, true);
calendarWrapper.setKalendarConfig(config);
return calendarWrapper;
}
public void deletePersonalCalendar(Identity identity) {
deleteCalendar(CalendarManager.TYPE_USER, identity.getName());
}
public void deleteGroupCalendar(BusinessGroup businessGroup) {
deleteCalendar(CalendarManager.TYPE_GROUP, businessGroup.getResourceableId().toString());
}
public void deleteCourseCalendar(ICourse course) {
deleteCalendar(CalendarManager.TYPE_COURSE, course.getResourceableId().toString());
}
public void deleteUserData(Identity identity, String newDeletedUserName) {
deletePersonalCalendar(identity);
Tracing.logDebug("Personal calendar deleted for identity=" + identity, this.getClass());
}
/**
* @see org.olat.commons.calendar.CalendarManager#addEventTo(org.olat.commons.calendar.model.Kalendar, org.olat.commons.calendar.model.KalendarEvent)
*/
public boolean addEventTo(final Kalendar cal, final KalendarEvent kalendarEvent) {
OLATResourceable calOres = getOresHelperFor(cal);
Boolean persistSuccessful = CoordinatorManager.getCoordinator().getSyncer().doInSync( calOres, new SyncerCallback<Boolean>() {
public Boolean execute() {
Kalendar loadedCal = getCalendarFromCache(cal.getType(),cal.getCalendarID());
loadedCal.addEvent(kalendarEvent);
boolean successfullyPersist = persistCalendar(loadedCal);
return new Boolean(successfullyPersist);
}
});
// inform all controller about calendar change for reload
CoordinatorManager.getCoordinator().getEventBus().fireEventToListenersOf(new KalendarModifiedEvent(cal), OresHelper.lookupType(CalendarManager.class));
return persistSuccessful.booleanValue();
}
/**
* @see org.olat.commons.calendar.CalendarManager#removeEventFrom(org.olat.commons.calendar.model.Kalendar, org.olat.commons.calendar.model.KalendarEvent)
*/
public boolean removeEventFrom(final Kalendar cal, final KalendarEvent kalendarEvent) {
OLATResourceable calOres = getOresHelperFor(cal);
Boolean removeSuccessful = CoordinatorManager.getCoordinator().getSyncer().doInSync( calOres, new SyncerCallback<Boolean>() {
public Boolean execute() {
Kalendar loadedCal = getCalendarFromCache(cal.getType(),cal.getCalendarID());
loadedCal.removeEvent(kalendarEvent);
boolean successfullyPersist = persistCalendar(loadedCal);
return new Boolean(successfullyPersist);
}
});
// inform all controller about calendar change for reload
CoordinatorManager.getCoordinator().getEventBus().fireEventToListenersOf(new KalendarModifiedEvent(cal), OresHelper.lookupType(CalendarManager.class));
return removeSuccessful.booleanValue();
}
/**
* @see org.olat.commons.calendar.CalendarManager#updateEventFrom(org.olat.commons.calendar.model.Kalendar, org.olat.commons.calendar.model.KalendarEvent)
*/
public boolean updateEventFrom(final Kalendar cal, final KalendarEvent kalendarEvent) {
OLATResourceable calOres = getOresHelperFor(cal);
Boolean updatedSuccessful = CoordinatorManager.getCoordinator().getSyncer().doInSync( calOres, new SyncerCallback<Boolean>() {
public Boolean execute() {
return updateEventAlreadyInSync(cal, kalendarEvent);
}
});
return updatedSuccessful.booleanValue();
}
/**
* @see org.olat.commons.calendar.CalendarManager#updateEventFrom(org.olat.commons.calendar.model.Kalendar, org.olat.commons.calendar.model.KalendarEvent)
*/
public boolean updateEventAlreadyInSync(final Kalendar cal, final KalendarEvent kalendarEvent) {
OLATResourceable calOres = getOresHelperFor(cal);
CoordinatorManager.getCoordinator().getSyncer().assertAlreadyDoInSyncFor(calOres);
Kalendar loadedCal = getCalendarFromCache(cal.getType(),cal.getCalendarID());
loadedCal.removeEvent(kalendarEvent); // remove old event
loadedCal.addEvent(kalendarEvent); // add changed event
boolean successfullyPersist = persistCalendar(loadedCal);
// inform all controller about calendar change for reload
CoordinatorManager.getCoordinator().getEventBus().fireEventToListenersOf(new KalendarModifiedEvent(cal), OresHelper.lookupType(CalendarManager.class));
return successfullyPersist;
}
/**
* Load a calendar when a calendar exists or create a new one.
* This method is not thread-safe. Must be called from synchronized block!
* @param callType
* @param callCalendarID
* @return
*/
protected Kalendar loadOrCreateCalendar(final String callType, final String callCalendarID) {
if (!calendarExists(callType, callCalendarID)) {
return createCalendar(callType, callCalendarID);
} else {
return loadCalendarFromFile(callType, callCalendarID);
}
}
}