/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 io.milton.mini.controllers;
import io.milton.annotations.CTag;
import io.milton.annotations.Calendars;
import io.milton.annotations.ChildOf;
import io.milton.annotations.ChildrenOf;
import io.milton.annotations.Delete;
import io.milton.annotations.Get;
import io.milton.annotations.ICalData;
import io.milton.annotations.MakeCalendar;
import io.milton.annotations.ModifiedDate;
import io.milton.annotations.Post;
import io.milton.annotations.Principal;
import io.milton.annotations.PutChild;
import io.milton.annotations.ResourceController;
import io.milton.annotations.UniqueId;
import io.milton.common.ModelAndView;
import io.milton.common.StringUtils;
import io.milton.http.Range;
import io.milton.http.Request;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.mini.DataSessionManager;
import io.milton.mini.services.CalendarService;
import io.milton.vfs.data.DataSession;
import io.milton.vfs.db.AttendeeRequest;
import io.milton.vfs.db.Branch;
import io.milton.vfs.db.CalEvent;
import io.milton.vfs.db.Calendar;
import io.milton.vfs.db.Profile;
import io.milton.vfs.db.utils.SessionManager;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.xml.namespace.QName;
import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.TimeZone;
import org.apache.commons.io.IOUtils;
import org.hibernate.Session;
@ResourceController
public class CalendarController {
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(CalendarController.class);
@Inject
private DataSessionManager dataSessionManager;
@Inject
private CalendarService calendarService;
private final List<String> simplifiedTimezoneList = new ArrayList<>();
public CalendarController() {
String[] temp = TimeZone.getAvailableIDs();
List<String> timezoneList = new ArrayList<>();
timezoneList.addAll(Arrays.asList(temp));
Collections.sort(timezoneList);
String filterList = "Canada|Mexico|Chile|Cuba|Brazil|Japan|Turkey|Mideast|Africa|America|Asia|Atlantic|Australia|Europe|Indian|Pacific";
Pattern p = Pattern.compile("^(" + filterList + ").*");
for (String tz : timezoneList) {
Matcher m = p.matcher(tz);
if (m.find()) {
simplifiedTimezoneList.add(tz);
}
}
}
@Get
public ModelAndView showUserCalendarsHome(CalendarsHome home, @Principal Profile user) {
List<AttendeeRequest> list = new ArrayList<>();
if (user.getAttendeeRequests() != null) {
for (AttendeeRequest ar : user.getAttendeeRequests()) {
// if (!ar.isAcknowledged()) {
list.add(ar);
// }
}
}
ModelAndView mav = new ModelAndView("invites", list, "calendarsHome");
String inboxCtag = calendarService.getCalendarInvitationsCTag(user);
mav.getModel().put("inboxCtag", inboxCtag);
return mav;
}
@ChildrenOf
public CalendarsHome getCalendarsHome(Profile user) {
return new CalendarsHome(user);
}
@ChildrenOf
@Calendars
public List<Calendar> getCalendars(CalendarsHome cals, @Principal Profile profile) throws NotAuthorizedException {
if (profile == null) {
log.warn("getCalendarrs with no user");
throw new NotAuthorizedException();
}
return cals.user.getCalendars();
}
@Get
public String showUserCalendar(Calendar home) {
System.out.println("show calendar");
return "calendar";
}
@Get(params = {"editMode"})
public ModelAndView showCalendarEditPage(Calendar calendar) {
System.out.println("show calendar edit page");
return new ModelAndView("profile", calendar, "calendarEditPage");
}
@ChildOf(pathSuffix = "new")
public Calendar createNewCalendar(CalendarsHome calendarsHome, String name, @Principal Profile currentUser) throws NotAuthorizedException {
if (currentUser == null) {
throw new NotAuthorizedException(null);
}
Session session = SessionManager.session();
Calendar newCal = calendarsHome.user.newCalendar(name, currentUser, SessionManager.session());
newCal.setTitle(name);
session.save(newCal);
return newCal;
}
@Post(bindData = true)
public Calendar saveCalendar(Calendar calendar) {
log.info("saveCalendar: " + calendar.getName());
SessionManager.session().save(calendar);
SessionManager.session().flush();
log.info("saved cal");
return calendar;
}
@MakeCalendar
public Calendar createNewCalendar(CalendarsHome calendarsHome, String newName, Map<QName, String> fieldsToSet, @Principal Profile currentUser) {
Calendar newCal = calendarsHome.user.newCalendar(newName, currentUser, SessionManager.session());
log.info("Create new calendar: " + newName);
for (QName qname : fieldsToSet.keySet()) {
log.info(" field: " + qname + " = " + fieldsToSet.get(qname));
}
SessionManager.session().save(newCal);
return newCal;
}
/**
* This should override the normal file handling from Repository (base class
* of Calendar)
*
* @param calendar
* @param request
* @return
*/
@ChildrenOf(override = true)
public List<CalEvent> getEvents(Calendar calendar, Request request) {
List<CalEvent> events = calendar.getEvents();
return events;
}
@ChildrenOf(override = true)
public List<AttendeeRequest> getAttendeeRequests(Calendar calendar, Request request) {
if (calendar.defaultCal()) {
try {
Profile user = (Profile) calendar.getBaseEntity();
return calendarService.getAttendeeRequests(user);
} catch (ClassCastException e) {
}
}
return null;
}
@ChildOf(pathSuffix = "new")
public CalEvent createNewEvent(Calendar calendar, String newName, @Principal Profile currentUser) throws NotAuthorizedException, IOException {
if (currentUser == null) {
throw new NotAuthorizedException(null);
}
newName = UUID.randomUUID().toString();
CalEvent newEvent = calendarService.createEvent(calendar, newName, null, null);
return newEvent;
}
@Get(params = {"editMode"})
public ModelAndView getEventEditPage(CalEvent event) {
ModelAndView mav = new ModelAndView("event", event, "eventEditPage");
mav.getModel().put("timezoneIdList", simplifiedTimezoneList);
return mav;
}
@Post(bindData = true, timeZoneParam = "timezone")
public CalEvent saveEvent(CalEvent event) {
log.info("saveEvent: " + event.getName());
SessionManager.session().save(event);
SessionManager.session().flush();
log.info("saved event: " + event.getId());
return event;
}
@Get
@ICalData
public void getEventIcal(CalEvent event, Calendar calendar, Request request, OutputStream out, Range range) throws IOException {
DataSession ds = dataSessionManager.get(request, calendar);
DataSession.FileNode fileNode = (DataSession.FileNode) ds.getRootDataNode().get(event.getName());
if (fileNode != null) {
if (range == null) {
fileNode.writeContent(out);
} else {
fileNode.writeContent(out, range.getStart(), range.getFinish());
}
} else {
String s = calendarService.getCalendarICal(event);
out.write(s.getBytes(StringUtils.UTF8));
}
}
@Get
@ICalData
public void getAttendeeRequestIcal(AttendeeRequest attendeeRequest, Request request, OutputStream out, Range range) throws IOException {
String s = calendarService.getInviteICal(attendeeRequest);
out.write(s.getBytes(StringUtils.UTF8));
}
/**
* Called on a PUT to an AttendeeRequest, which is typically when the user
* is responding that they are attending or not
*
* @param attendeeReq
* @param request
*/
@PutChild
public Object respondToAttendanceInvite(AttendeeRequest attendeeReq, Calendar calendar, Request request, InputStream inputStream) throws IOException, ParserException {
log.info("respondToAttendanceInvite");
ByteArrayOutputStream bout = new ByteArrayOutputStream();
IOUtils.copy(inputStream, bout);
System.out.println(bout.toString());
net.fortuna.ical4j.model.Calendar cal = calendarService.parse(new ByteArrayInputStream(bout.toByteArray()));
CalEvent newEvent = calendarService.processResponse(attendeeReq, calendar, cal, SessionManager.session());
if (newEvent != null) {
return newEvent;
} else {
return attendeeReq;
}
}
@PutChild
public CalEvent createEvent(Calendar calendar, final String newName, InputStream inputStream, Request request, final @Principal Profile principal) throws IOException {
log.info("createNew: set content");
ByteArrayOutputStream bout = new ByteArrayOutputStream();
IOUtils.copy(inputStream, bout);
final DataSession ds = dataSessionManager.get(request, calendar, true, principal);
String icalData = bout.toString(StringUtils.UTF8.name());
CalEvent newEvent = calendarService.createEvent(calendar, newName, icalData, new CalendarService.UpdatedEventCallback() {
@Override
public void updated(String updatedIcal, CalEvent e) throws IOException {
DataSession.FileNode newFileNode = (DataSession.FileNode) ds.getRootDataNode().get(newName);
if (newFileNode == null) { // usually should be null
newFileNode = ds.getRootDataNode().addFile(newName);
}
newFileNode.setContent(new ByteArrayInputStream(updatedIcal.getBytes(StringUtils.UTF8)));
ds.save(principal);
}
});
return newEvent;
}
@PutChild
public CalEvent updateEvent(CalEvent event, InputStream inputStream, final Request request, Calendar calendar, final @Principal Profile principal) throws IOException {
log.info("updateEvent: set content");
ByteArrayOutputStream bout = new ByteArrayOutputStream();
IOUtils.copy(inputStream, bout);
DataSession ds = dataSessionManager.get(request, calendar, true, principal);
DataSession.FileNode fileNode = (DataSession.FileNode) ds.getRootDataNode().get(event.getName());
if (fileNode == null) {
fileNode = ds.getRootDataNode().addFile(event.getName());
}
fileNode.setContent(new ByteArrayInputStream(bout.toByteArray()));
ds.save(principal);
String icalData = bout.toString(StringUtils.UTF8.name());
calendarService.update(event, icalData, new CalendarService.UpdatedAttendeeCallback() {
@Override
public void deleted(CalEvent deleted) throws IOException {
log.info("deleted: " + deleted.getName() + " - " + deleted.getSummary());
Calendar calAttendee = deleted.getCalendar();
DataSession ds = dataSessionManager.get(request, calAttendee, true, principal);
DataSession.FileNode newFileNode = (DataSession.FileNode) ds.getRootDataNode().get(deleted.getName());
if (newFileNode != null) { // usually should be null
newFileNode.delete();
ds.save(principal);
}
}
@Override
public void updated(CalEvent updated) throws IOException {
log.info("updated: " + updated.getName() + " - " + updated.getSummary());
Calendar calAttendee = updated.getCalendar();
DataSession ds = dataSessionManager.get(request, calAttendee, true, principal);
DataSession.FileNode newFileNode = (DataSession.FileNode) ds.getRootDataNode().get(updated.getName());
if (newFileNode != null) { // usually should be null
ByteArrayOutputStream oldContent = new ByteArrayOutputStream();
newFileNode.writeContent(oldContent);
net.fortuna.ical4j.model.Calendar oldCal;
try {
oldCal = calendarService.parse(new ByteArrayInputStream(oldContent.toByteArray()));
} catch (ParserException ex) {
throw new RuntimeException(ex);
}
String oldCalText = calendarService.formatIcal(oldCal);
System.out.println("OLD CAL-----------");
System.out.println(oldCalText);
String newIcal = calendarService.updateIcalText(updated, oldCal);
System.out.println("NEW CAL --------");
System.out.println(newIcal);
newFileNode.setContent(new ByteArrayInputStream(newIcal.getBytes(StringUtils.UTF8)));
ds.save(principal);
}
}
});
return event;
}
@Delete
public void deleteEvent(CalEvent event, Request request, Calendar calendar, @Principal Profile principal) throws IOException {
log.info("deleteEvent: " + event.getName());
Session session = SessionManager.session();
calendarService.delete(event);
DataSession ds = dataSessionManager.get(request, calendar, true, principal);
DataSession.FileNode fileNode = (DataSession.FileNode) ds.getRootDataNode().get(event.getName());
if (fileNode != null) {
fileNode.delete();
}
ds.save(principal);
session.flush();
}
@ModifiedDate
public Date getEventModifiedDate(CalEvent event) {
return event.getModifiedDate();
}
@UniqueId
public String getEventId(CalEvent event) {
if (event.getId() != null) {
return event.getId().toString();
} else {
return null;
}
}
@UniqueId
public String getCalendarUniqueId(Calendar cal) {
return cal.getId() + "";
}
@ModifiedDate
public Date getCalendarModDate(Calendar cal) {
// return the mod date of the branch
Branch b = cal.liveBranch();
if (b != null) {
return b.getHead().getCreatedDate();
}
return null;
}
@CTag
public String getCalendarCTag(Calendar cal) {
// return the hash of the branch
Branch b = cal.liveBranch();
if (b != null) {
return b.getHead().getItemHash();
}
return null;
}
public class CalendarsHome {
private final Profile user;
public CalendarsHome(Profile user) {
this.user = user;
}
public String getName() {
return "cals";
}
}
}