/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig 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 org.jasig.schedassist.impl.caldav;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.TimeZone;
import net.fortuna.ical4j.model.TimeZoneRegistry;
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.Role;
import net.fortuna.ical4j.model.property.Attendee;
import net.fortuna.ical4j.model.property.DtEnd;
import net.fortuna.ical4j.model.property.DtStart;
import net.fortuna.ical4j.model.property.Organizer;
import net.fortuna.ical4j.model.property.Status;
import net.fortuna.ical4j.model.property.Transp;
import net.fortuna.ical4j.model.property.Uid;
import org.apache.commons.lang.Validate;
import org.jasig.schedassist.IAffiliationSource;
import org.jasig.schedassist.model.AppointmentRole;
import org.jasig.schedassist.model.AvailableBlock;
import org.jasig.schedassist.model.DefaultEventUtilsImpl;
import org.jasig.schedassist.model.ICalendarAccount;
import org.jasig.schedassist.model.IScheduleOwner;
import org.jasig.schedassist.model.IScheduleVisitor;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
/**
* Subclass of {@link DefaultEventUtilsImpl} specific for
* caldav.
*
* The explicitSetTimeZone field is of significance. If set to true, one must also set
* the timeZone property. The {@link InitializingBean#afterPropertiesSet()} implementation
* on this class should be called to verify state (this is automatically called by the Spring IoC
* container; only call this method explicitly specifically if not being constructed by Spring).
*
* When true and the timezone can be resolved, the behavior of {@link #wrapEventInCalendar(VEvent)} and
* {@link #constructAvailableAppointment(AvailableBlock, IScheduleOwner, IScheduleVisitor, String)} will change.
*
* The explicitSetTimeZone and timeZone properties are encouraged when integrating with an Oracle Communications Suite
* environment.
*
* @author Nicholas Blair, npblair@wisc.edu
*/
public class CaldavEventUtilsImpl extends DefaultEventUtilsImpl implements InitializingBean {
private boolean explicitSetTimeZone = false;
private String timeZone;
private TimeZone _timeZone;
/**
*
* @param affiliationSource
*/
public CaldavEventUtilsImpl(IAffiliationSource affiliationSource) {
super(affiliationSource);
}
/**
* @return the explicitSetTimeZone
*/
public boolean isExplicitSetTimeZone() {
return explicitSetTimeZone;
}
/**
* @param explicitSetTimeZone the explicitSetTimeZone to set
*/
@Value("${caldav.explicitSetTimeZone:false}")
public void setExplicitSetTimeZone(boolean explicitSetTimeZone) {
this.explicitSetTimeZone = explicitSetTimeZone;
}
/**
* @return the timeZone
*/
public String getTimeZone() {
return timeZone;
}
/**
* @param timeZone the timeZone to set
*/
@Value("${caldav.systemTimeZone:America/Chicago}")
public void setTimeZone(String timeZone) {
this.timeZone = timeZone;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
if(isExplicitSetTimeZone()) {
Validate.notEmpty(this.timeZone, "timeZone field cannot be empty if explicitSetTimeZone is true");
TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
_timeZone = registry.getTimeZone(this.timeZone);
if(null == _timeZone) {
throw new IllegalStateException("no timezone found for " + timeZone);
}
}
}
/**
* Calls the super implementation, and adds an {@link Organizer} and an {@link Uid}.
* If the explicitSetTimeZone field is true and the corresponding timeZone can be resolved,
* the {@link DtStart} and {@link DtEnd} are modified to include the corresponding TZID parameter.
*
* @see #constructOrganizer(ICalendarAccount)
* @see #generateNewUid()
* (non-Javadoc)
* @see org.jasig.schedassist.model.DefaultEventUtilsImpl#constructAvailableAppointment(org.jasig.schedassist.model.AvailableBlock, org.jasig.schedassist.model.IScheduleOwner, org.jasig.schedassist.model.IScheduleVisitor, java.lang.String)
*/
@Override
public VEvent constructAvailableAppointment(AvailableBlock block,
IScheduleOwner owner, IScheduleVisitor visitor,
String eventDescription) {
VEvent event = super.constructAvailableAppointment(block, owner, visitor,
eventDescription);
if(isExplicitSetTimeZone() && _timeZone != null) {
DtStart start = event.getStartDate();
start.setTimeZone(_timeZone);
DtEnd end = event.getEndDate();
end.setTimeZone(_timeZone);
}
return event;
}
/* (non-Javadoc)
* @see org.jasig.schedassist.model.DefaultEventUtilsImpl#willEventCauseConflict(org.jasig.schedassist.model.ICalendarAccount, net.fortuna.ical4j.model.component.VEvent)
*/
@Override
public boolean willEventCauseConflict(ICalendarAccount calendarAccount,
VEvent event) {
if(event == null) {
return false;
}
Status status = event.getStatus();
if(status != null && Status.VEVENT_CANCELLED.equals(status)) {
return false;
}
Transp transp = event.getTransparency();
if(null == transp) {
return true;
} else {
return Transp.OPAQUE.equals(transp);
}
}
/* (non-Javadoc)
* @see org.jasig.schedassist.model.DefaultEventUtilsImpl#constructSchedulingAssistantAttendee(org.jasig.schedassist.model.ICalendarAccount, org.jasig.schedassist.model.AppointmentRole)
*/
@Override
public Attendee constructSchedulingAssistantAttendee(
ICalendarAccount calendarAccount, AppointmentRole role) {
Attendee attendee = super.constructSchedulingAssistantAttendee(calendarAccount, role);
if(AppointmentRole.VISITOR.equals(role)) {
attendee.getParameters().add(Role.REQ_PARTICIPANT);
} else if (AppointmentRole.OWNER.equals(role)) {
attendee.getParameters().add(Role.CHAIR);
}
return attendee;
}
/* (non-Javadoc)
* @see org.jasig.schedassist.model.DefaultEventUtilsImpl#convertBlockToReflectionEvent(org.jasig.schedassist.model.AvailableBlock)
*/
@Override
protected VEvent convertBlockToReflectionEvent(AvailableBlock block) {
VEvent reflection = super.convertBlockToReflectionEvent(block);
reflection.getProperties().add(this.generateNewUid());
return reflection;
}
/**
* If the explicitSetTimeZone field is true and the corresponding timeZone can be resolved,
* the corresponding {@link VTimeZone} is added to the returned {@link Calendar}.
*
* (non-Javadoc)
* @see org.jasig.schedassist.model.DefaultEventUtilsImpl#wrapEventInCalendar(net.fortuna.ical4j.model.component.VEvent)
*/
@Override
public Calendar wrapEventInCalendar(VEvent event) {
Calendar calendar = super.wrapEventInCalendar(event);
if(isExplicitSetTimeZone() && this._timeZone != null) {
calendar.getComponents().add(this._timeZone.getVTimeZone());
}
return calendar;
}
}