/*
* Copyright 2016 EuregJUG.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.euregjug.site.web;
import eu.euregjug.site.events.EventEntity;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.AbstractView;
import org.springframework.web.util.UriComponentsBuilder;
/**
* Super simple (if not primitive) iCal / ics View for EuregJUG events.
*
* @author Michael J. Simons, 2016-01-04
*/
@Component("events.ics")
final class EventsIcalView extends AbstractView {
public static final String ICS_LINEBREAK = "\r\n";
private static final int MAX_LINE_LENGTH = 75;
private final DateTimeFormatter tstampFormat = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'", Locale.ENGLISH).withZone(ZoneId.of("UTC"));
private final ZoneId zoneId = ZoneId.systemDefault();
EventsIcalView() {
super.setContentType("text/calendar");
}
String formatLine(final StringBuilder s) {
final int l = s.length() / MAX_LINE_LENGTH;
int cnt = 0;
for (int i = 1; i <= l; ++i) {
s.insert(i * MAX_LINE_LENGTH + (ICS_LINEBREAK.length() * cnt++), ICS_LINEBREAK + " ");
}
return s.toString();
}
@Override
protected void renderMergedOutputModel(final Map<String, Object> model, final HttpServletRequest request, final HttpServletResponse response) throws IOException {
final List<EventEntity> events = (List<EventEntity>) model.get("events");
super.setResponseContentType(request, response);
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8))) {
w.write("BEGIN:VCALENDAR" + ICS_LINEBREAK);
w.write("VERSION:2.0" + ICS_LINEBREAK);
w.write("PRODID:http://www.euregjug.eu/events" + ICS_LINEBREAK);
for (EventEntity event : events) {
final StringBuilder summaryBuilder = new StringBuilder("SUMMARY:").append(event.getName());
if (event.getSpeaker() != null) {
summaryBuilder.append(" (").append(event.getSpeaker()).append(")");
}
final ZonedDateTime heldOn = event.getHeldOn().toInstant().atZone(zoneId);
w.write("BEGIN:VEVENT" + ICS_LINEBREAK);
w.write(String.format("UID:%d@euregjug.eu%s", event.getId(), ICS_LINEBREAK));
w.write("ORGANIZER:EuregJUG" + ICS_LINEBREAK);
w.write("DTSTAMP:" + tstampFormat.format(event.getCreatedAt().toInstant().atZone(zoneId)) + ICS_LINEBREAK);
w.write("DTSTART:" + tstampFormat.format(heldOn) + ICS_LINEBREAK);
w.write("DTEND:" + tstampFormat.format(heldOn.plusMinutes(Optional.ofNullable(event.getDuration()).orElse(120))) + ICS_LINEBREAK);
w.write(formatLine(summaryBuilder) + ICS_LINEBREAK);
w.write(formatLine(new StringBuilder("DESCRIPTION:").append(event.getDescription())) + ICS_LINEBREAK);
w.write("URL:" + UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request)).replacePath("/register/{eventId}").buildAndExpand(event.getId()) + ICS_LINEBREAK);
if (event.getLocation() != null) {
w.write("LOCATION: " + event.getLocation().replaceAll("\\n+", ", ") + ICS_LINEBREAK);
}
w.write("END:VEVENT" + ICS_LINEBREAK);
}
w.write("END:VCALENDAR" + ICS_LINEBREAK);
}
response.flushBuffer();
}
}