diff --git a/src/main/java/org/olat/commons/calendar/ICalServlet.java b/src/main/java/org/olat/commons/calendar/ICalServlet.java index 77e2a552c482f27f78c0addea319aa906f6e81df..42879439b156ddad66e5df0e306839d98e703c73 100644 --- a/src/main/java/org/olat/commons/calendar/ICalServlet.java +++ b/src/main/java/org/olat/commons/calendar/ICalServlet.java @@ -56,12 +56,12 @@ import org.olat.core.util.StringHelper; import org.olat.core.util.i18n.I18nManager; import net.fortuna.ical4j.data.CalendarBuilder; -import net.fortuna.ical4j.data.CalendarOutputter; import net.fortuna.ical4j.data.ParserException; import net.fortuna.ical4j.model.Calendar; import net.fortuna.ical4j.model.Component; import net.fortuna.ical4j.model.ComponentList; import net.fortuna.ical4j.model.Parameter; +import net.fortuna.ical4j.model.Property; import net.fortuna.ical4j.model.PropertyList; import net.fortuna.ical4j.model.ValidationException; import net.fortuna.ical4j.model.component.VEvent; @@ -83,13 +83,15 @@ import net.fortuna.ical4j.util.Strings; * Initial Date: June 1, 2008 * * @author Udit Sajjanhar + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ public class ICalServlet extends HttpServlet { private static final long serialVersionUID = -155266285395912535L; private static final OLog log = Tracing.createLoggerFor(ICalServlet.class); - private final int cacheAge = 60 * 60 * 12; + private static final int TTL_HOURS = 6; + private static final int cacheAge = 60 * 60 * TTL_HOURS;//6 Hours private static final ConcurrentMap<String,VTimeZone> outlookVTimeZones = new ConcurrentHashMap<>(); /** collection of iCal feed prefixs **/ @@ -226,9 +228,8 @@ public class ICalServlet extends HttpServlet { } else { // read and return the calendar file Calendar calendar = calendarManager.readCalendar(calendarType, calendarID); - updateUrlProperties(calendar); DBFactory.getInstance().commitAndCloseSession(); - new CalendarOutputter(false).output(calendar, response.getOutputStream()); + outputCalendar(calendar, request, response); } } else { response.sendError(HttpServletResponse.SC_NOT_FOUND, requestUrl); @@ -241,6 +242,59 @@ public class ICalServlet extends HttpServlet { httpResponse.setHeader("Cache-Control", "max-age="+ cacheAge); } + private void outputCalendar(Calendar calendar, HttpServletRequest request, HttpServletResponse response) + throws ValidationException, IOException { + boolean outlook = isOutlook(request); + updateUrlProperties(calendar); + + Writer out = response.getWriter(); + out.write(Calendar.BEGIN); + out.write(':'); + out.write(Calendar.VCALENDAR); + out.write(Strings.LINE_SEPARATOR); + out.write(Version.VERSION_2_0.toString()); + + boolean calScale = false; + for (Iterator<?> propIter = calendar.getProperties().iterator(); propIter.hasNext();) { + Object pobject = propIter.next(); + if(pobject instanceof Property) { + Property property = (Property)pobject; + if(Version.VERSION.equals(property.getName())) { + //we force version 2.0 + } else if(Version.CALSCALE.equals(property.getName())) { + out.write(property.toString()); + calScale = true; + } else { + out.write(property.toString()); + } + } + } + + if(!calScale) { + out.write(CalScale.GREGORIAN.toString()); + } + + outputTTL(out); + + Set<String> timezoneIds = new HashSet<>(); + outputCalendarComponents(calendar, out, outlook, timezoneIds); + if(outlook) { + outputTimeZoneForOutlook(timezoneIds, out); + } + + out.write(Calendar.END); + out.write(':'); + out.write(Calendar.VCALENDAR); + } + + /** + * Collect all the calendars, update the URL properties and the UUID. + * + * @param identity + * @param request + * @param response + * @throws IOException + */ private void generateAggregatedCalendar(Identity identity, HttpServletRequest request, HttpServletResponse response) throws IOException { PersonalCalendarManager homeCalendarManager = CoreSpringFactory.getImpl(PersonalCalendarManager.class); if(identity == null) { @@ -257,6 +311,8 @@ public class ICalServlet extends HttpServlet { out.write(Strings.LINE_SEPARATOR); out.write(Version.VERSION_2_0.toString()); out.write(CalScale.GREGORIAN.toString()); + + outputTTL(out); Set<String> timezoneIds = new HashSet<>(); int numOfFiles = iCalFiles.size(); @@ -281,6 +337,22 @@ public class ICalServlet extends HttpServlet { return false; } + /** + * Append TTL:<br> + * @see http://stackoverflow.com/questions/17152251/specifying-name-description-and-refresh-interval-in-ical-ics-format + * @see http://tools.ietf.org/html/draft-daboo-icalendar-extensions-06 + * + * @param out + * @throws IOException + */ + private void outputTTL(Writer out) + throws IOException { + out.write("X-PUBLISHED-TTL:PT" + TTL_HOURS + "H"); + out.write(Strings.LINE_SEPARATOR); + out.write("REFRESH-INTERVAL;VALUE=DURATION:PT" + TTL_HOURS + "H"); + out.write(Strings.LINE_SEPARATOR); + } + private void outputTimeZoneForOutlook(Set<String> timezoneIds, Writer out) { for(String timezoneId:timezoneIds) { if(StringHelper.containsNonWhitespace(timezoneId)) { @@ -306,6 +378,15 @@ public class ICalServlet extends HttpServlet { String prefix = fileInfos.getType() + "-" + fileInfos.getCalendarId() + "-"; updateUUID(calendar, prefix); + outputCalendarComponents(calendar, out, outlook, timezoneIds); + } catch (IOException | OLATRuntimeException e) { + log.error("", e); + } + } + + private void outputCalendarComponents(Calendar calendar, Writer out, boolean outlook, Set<String> timezoneIds) + throws IOException { + try { ComponentList events = calendar.getComponents(); for (final Iterator<?> i = events.iterator(); i.hasNext();) { Object comp = i.next(); diff --git a/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarManager.java b/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarManager.java index c1bc899822387f506ba22c16da5621c799ae230a..78faf4d14556d021efe529f44879ea6f1a70ce8f 100644 --- a/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarManager.java +++ b/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarManager.java @@ -244,9 +244,10 @@ public class ICalFileCalendarManager implements CalendarManager, InitializingBea return cal; } - /** - * Internal read calendar file from filesystem - */ + /** + * Internal read calendar file from filesystem. It doesn't + * use the cache and return a not shared calendar. + */ @Override public Calendar readCalendar(String type, String calendarID) { if(log.isDebug()) { diff --git a/src/test/java/org/olat/selenium/page/course/GroupTaskConfigurationPage.java b/src/test/java/org/olat/selenium/page/course/GroupTaskConfigurationPage.java index 5baaa29cb2941639ba1b9843ce7a3e2633672ec6..a40e53ece3b7914fa7cd2263e225c35eb72136b8 100644 --- a/src/test/java/org/olat/selenium/page/course/GroupTaskConfigurationPage.java +++ b/src/test/java/org/olat/selenium/page/course/GroupTaskConfigurationPage.java @@ -65,6 +65,7 @@ public class GroupTaskConfigurationPage { By chooseGroupBy = By.cssSelector("a.o_form_groupchooser"); browser.findElement(chooseGroupBy).click(); OOGraphene.waitBusy(browser); + OOGraphene.waitModalDialog(browser); return this; } @@ -72,6 +73,7 @@ public class GroupTaskConfigurationPage { By createGroupBy = By.cssSelector("div.o_button_group_right a"); browser.findElement(createGroupBy).click(); OOGraphene.waitBusy(browser); + OOGraphene.waitModalDialog(browser); //fill the form By nameBy = By.cssSelector(".o_sel_group_edit_title input[type='text']");