use of com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence in project zm-mailbox by Zimbra.
the class ToXML method encodeCalendarItemRecur.
public static void encodeCalendarItemRecur(Element parent, CalendarItem calItem) {
TimeZoneMap tzmap = calItem.getTimeZoneMap();
encodeTimeZoneMap(parent, tzmap);
Invite[] invites = calItem.getInvites();
for (Invite inv : invites) {
String elemName;
if (inv.isCancel()) {
elemName = MailConstants.E_CAL_CANCEL;
} else if (inv.hasRecurId()) {
elemName = MailConstants.E_CAL_EXCEPT;
} else {
elemName = MailConstants.E_INVITE_COMPONENT;
}
Element compElem = parent.addElement(elemName);
boolean allDay = inv.isAllDayEvent();
// RECURRENCE-ID
if (inv.hasRecurId())
encodeRecurId(compElem, inv.getRecurId(), allDay);
if (!inv.isCancel()) {
// DTSTART
ParsedDateTime dtStart = inv.getStartTime();
if (dtStart != null)
encodeDtStart(compElem, dtStart, allDay, false);
// DTEND or DURATION
ParsedDateTime dtEnd = inv.getEndTime();
if (dtEnd != null) {
encodeDtEnd(compElem, dtEnd, allDay, inv.isTodo(), false);
} else {
ParsedDuration dur = inv.getDuration();
if (dur != null)
dur.toXml(compElem);
}
// recurrence definition
IRecurrence recurrence = inv.getRecurrence();
if (recurrence != null) {
Element recurElem = compElem.addElement(MailConstants.E_CAL_RECUR);
recurrence.toXml(recurElem);
}
}
}
}
use of com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence in project zm-mailbox by Zimbra.
the class ExpandRecur method parseRecur.
protected static ParsedRecurrence parseRecur(Element request, TimeZoneMap tzmap) throws ServiceException {
CalendarUtils.parseTimeZones(request, tzmap);
IRecurrence recurrence = null;
List<IException> exceptions = new ArrayList<IException>();
for (Iterator<Element> compIter = request.elementIterator(); compIter.hasNext(); ) {
Element elem = compIter.next();
String elemName = elem.getName();
boolean isCancel = false;
if (MailConstants.E_CAL_CANCEL.equals(elemName)) {
isCancel = true;
} else if (!MailConstants.E_INVITE_COMPONENT.equals(elemName) && !MailConstants.E_CAL_EXCEPT.equals(elemName)) {
continue;
}
RecurId recurId = null;
Element recurIdElem = elem.getOptionalElement(MailConstants.E_CAL_EXCEPTION_ID);
if (recurIdElem != null)
recurId = CalendarUtils.parseRecurId(recurIdElem, tzmap);
if (!isCancel) {
ParsedDateTime dtStart = null;
Element dtStartElem = elem.getElement(MailConstants.E_CAL_START_TIME);
dtStart = CalendarUtils.parseDateTime(dtStartElem, tzmap);
ParsedDateTime dtEnd = null;
Element dtEndElem = elem.getOptionalElement(MailConstants.E_CAL_END_TIME);
if (dtEndElem != null)
dtEnd = CalendarUtils.parseDateTime(dtEndElem, tzmap);
ParsedDuration dur = null;
Element durElem = elem.getOptionalElement(MailConstants.E_CAL_DURATION);
if (durElem != null)
dur = ParsedDuration.parse(durElem);
if (dtEnd == null && dur == null)
throw ServiceException.INVALID_REQUEST("Must specify either " + MailConstants.E_CAL_END_TIME + " or " + MailConstants.E_CAL_DURATION + " in " + elemName, null);
Element recurElem = elem.getOptionalElement(MailConstants.E_CAL_RECUR);
if (recurElem != null) {
// series with a rule
recurrence = CalendarUtils.parseRecur(recurElem, tzmap, dtStart, dtEnd, dur, recurId);
} else {
// modified instance, or it has no rule and no recurrence-id
if (dur == null && dtStart != null && dtEnd != null)
dur = dtEnd.difference(dtStart);
if (recurId == null)
recurId = new RecurId(dtStart, RecurId.RANGE_NONE);
exceptions.add(new ExceptionRule(recurId, dtStart, dur, null));
}
} else if (recurId != null) {
// canceled instance
exceptions.add(new CancellationRule(recurId));
}
}
ParsedRecurrence parsed = new ParsedRecurrence();
if (recurrence instanceof RecurrenceRule) {
RecurrenceRule rrule = (RecurrenceRule) recurrence;
for (IException exception : exceptions) {
rrule.addException(exception);
}
parsed.rrule = rrule;
} else {
parsed.exceptions = exceptions;
}
return parsed;
}
use of com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence in project zm-mailbox by Zimbra.
the class WorkingHours method getWorkingHours.
public static FreeBusy getWorkingHours(Account authAcct, boolean asAdmin, Account account, String name, long start, long end) throws ServiceException {
// If free/busy viewing is blocked, so is viewing working hours.
AccessManager accessMgr = AccessManager.getInstance();
boolean accountAceAllowed = accessMgr.canDo(authAcct, account, User.R_viewFreeBusy, asAdmin);
if (!accountAceAllowed)
return FreeBusy.nodataFreeBusy(account.getName(), start, end);
// Get the working hours preference and parse it.
String workingHoursPref = account.getPrefCalendarWorkingHours();
HoursByDay workingHoursByDay = new HoursByDay(workingHoursPref);
// Build a recurrence rule for each day of the week and expand over the time range.
IntervalList intervals = new IntervalList(start, end);
ICalTimeZone tz = Util.getAccountTimeZone(account);
TimeZoneMap tzmap = new TimeZoneMap(tz);
StartSpec startSpec = new StartSpec(start, tz);
for (int day = 1; day <= 7; ++day) {
TimeRange timeRange = workingHoursByDay.getHoursForDay(day);
if (timeRange.enabled) {
IRecurrence rrule = getRecurrenceForDay(day, startSpec, timeRange, tz, tzmap);
List<Instance> instances = rrule.expandInstances(0, start, end);
for (Instance inst : instances) {
Interval ival = new Interval(inst.getStart(), inst.getEnd(), IcalXmlStrMap.FBTYPE_BUSY_UNAVAILABLE);
intervals.addInterval(ival);
}
}
}
// hours are shown as out-of-office.
for (Iterator<Interval> iter = intervals.iterator(); iter.hasNext(); ) {
Interval interval = iter.next();
String status = interval.getStatus();
interval.setStatus(invertStatus(status));
}
return new FreeBusy(name, intervals, start, end);
}
use of com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence in project zm-mailbox by Zimbra.
the class CalendarUtils method parseRecur.
static Recurrence.IRecurrence parseRecur(Element recurElt, TimeZoneMap invTzMap, ParsedDateTime dtStart, ParsedDateTime dtEnd, ParsedDuration dur, RecurId recurId) throws ServiceException {
if (dur == null && dtStart != null && dtEnd != null)
dur = dtEnd.difference(dtStart);
ArrayList<IRecurrence> addRules = new ArrayList<IRecurrence>();
ArrayList<IRecurrence> subRules = new ArrayList<IRecurrence>();
for (Iterator iter = recurElt.elementIterator(); iter.hasNext(); ) {
Element e = (Element) iter.next();
boolean exclude = false;
if (e.getName().equals(MailConstants.E_CAL_EXCLUDE)) {
exclude = true;
} else {
if (!e.getName().equals(MailConstants.E_CAL_ADD)) {
continue;
}
}
for (Iterator intIter = e.elementIterator(); intIter.hasNext(); ) {
Element intElt = (Element) intIter.next();
if (intElt.getName().equals(MailConstants.E_CAL_DATES)) {
// handle RDATE or EXDATE
String tzid = intElt.getAttribute(MailConstants.A_CAL_TIMEZONE, null);
ICalTimeZone tz = tzid != null ? invTzMap.lookupAndAdd(tzid) : null;
RdateExdate rexdate = new RdateExdate(exclude ? ICalTok.EXDATE : ICalTok.RDATE, tz);
ICalTok valueType = null;
for (Iterator<Element> dtvalIter = intElt.elementIterator(MailConstants.E_CAL_DATE_VAL); dtvalIter.hasNext(); ) {
ICalTok dtvalValueType = null;
Element dtvalElem = dtvalIter.next();
Element dtvalStartElem = dtvalElem.getElement(MailConstants.E_CAL_START_TIME);
String dtvalStartDateStr = dtvalStartElem.getAttribute(MailConstants.A_CAL_DATETIME);
ParsedDateTime dtvalStart = parseDateTime(dtvalElem.getName(), dtvalStartDateStr, tzid, invTzMap);
Element dtvalEndElem = dtvalElem.getOptionalElement(MailConstants.E_CAL_END_TIME);
Element dtvalDurElem = dtvalElem.getOptionalElement(MailConstants.E_CAL_DURATION);
if (dtvalEndElem == null && dtvalDurElem == null) {
if (dtvalStart.hasTime())
dtvalValueType = ICalTok.DATE_TIME;
else
dtvalValueType = ICalTok.DATE;
rexdate.addValue(dtvalStart);
} else {
dtvalValueType = ICalTok.PERIOD;
if (dtvalEndElem != null) {
String dtvalEndDateStr = dtvalEndElem.getAttribute(MailConstants.A_CAL_DATETIME);
ParsedDateTime dtvalEnd = parseDateTime(dtvalElem.getName(), dtvalEndDateStr, tzid, invTzMap);
Period p = new Period(dtvalStart, dtvalEnd);
rexdate.addValue(p);
} else {
ParsedDuration d = ParsedDuration.parse(dtvalDurElem);
Period p = new Period(dtvalStart, d);
rexdate.addValue(p);
}
}
if (valueType == null) {
valueType = dtvalValueType;
rexdate.setValueType(valueType);
} else if (valueType != dtvalValueType)
throw ServiceException.INVALID_REQUEST("Cannot mix different value types in a single <" + intElt.getName() + "> element", null);
}
Recurrence.SingleDates sd = new Recurrence.SingleDates(rexdate, dur);
if (exclude)
subRules.add(sd);
else
addRules.add(sd);
} else if (intElt.getName().equals(MailConstants.E_CAL_RULE)) {
// handle RRULE or EXRULE
// Turn XML into iCal RECUR string, which will then be
// parsed by ical4j Recur object.
StringBuilder recurBuf = new StringBuilder(100);
String freq = IcalXmlStrMap.sFreqMap.toIcal(intElt.getAttribute(MailConstants.A_CAL_RULE_FREQ));
recurBuf.append("FREQ=").append(freq);
for (Iterator ruleIter = intElt.elementIterator(); ruleIter.hasNext(); ) {
Element ruleElt = (Element) ruleIter.next();
String ruleEltName = ruleElt.getName();
if (ruleEltName.equals(MailConstants.E_CAL_RULE_UNTIL)) {
recurBuf.append(";UNTIL=");
String d = ruleElt.getAttribute(MailConstants.A_CAL_DATETIME);
recurBuf.append(d);
// (RFC2445 Section 4.3.10 Recurrence Rule)
if (d.indexOf("T") >= 0)
if (d.indexOf("Z") < 0)
recurBuf.append('Z');
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_COUNT)) {
int num = (int) ruleElt.getAttributeLong(MailConstants.A_CAL_RULE_COUNT_NUM, -1);
if (num > 0) {
recurBuf.append(";COUNT=").append(num);
} else {
throw ServiceException.INVALID_REQUEST("Expected positive num attribute in <recur> <rule> <count>", null);
}
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_INTERVAL)) {
String ival = ruleElt.getAttribute(MailConstants.A_CAL_RULE_INTERVAL_IVAL);
recurBuf.append(";INTERVAL=").append(ival);
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYSECOND)) {
String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYSECOND_SECLIST);
recurBuf.append(";BYSECOND=").append(list);
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYMINUTE)) {
String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYMINUTE_MINLIST);
recurBuf.append(";BYMINUTE=").append(list);
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYHOUR)) {
String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYHOUR_HRLIST);
recurBuf.append(";BYHOUR=").append(list);
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYDAY)) {
recurBuf.append(";BYDAY=");
int pos = 0;
for (Iterator bydayIter = ruleElt.elementIterator(MailConstants.E_CAL_RULE_BYDAY_WKDAY); bydayIter.hasNext(); pos++) {
Element wkdayElt = (Element) bydayIter.next();
if (pos > 0)
recurBuf.append(",");
String ordwk = wkdayElt.getAttribute(MailConstants.A_CAL_RULE_BYDAY_WKDAY_ORDWK, null);
if (ordwk != null)
recurBuf.append(ordwk);
String day = wkdayElt.getAttribute(MailConstants.A_CAL_RULE_DAY);
if (day == null || day.length() == 0)
throw ServiceException.INVALID_REQUEST("Missing " + MailConstants.A_CAL_RULE_DAY + " in <" + ruleEltName + ">", null);
recurBuf.append(day);
}
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYMONTHDAY)) {
String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYMONTHDAY_MODAYLIST);
recurBuf.append(";BYMONTHDAY=").append(list);
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYYEARDAY)) {
String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYYEARDAY_YRDAYLIST);
recurBuf.append(";BYYEARDAY=").append(list);
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYWEEKNO)) {
String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYWEEKNO_WKLIST);
recurBuf.append(";BYWEEKNO=").append(list);
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYMONTH)) {
String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYMONTH_MOLIST);
recurBuf.append(";BYMONTH=").append(list);
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYSETPOS)) {
String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYSETPOS_POSLIST);
recurBuf.append(";BYSETPOS=").append(list);
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_WKST)) {
String day = ruleElt.getAttribute(MailConstants.A_CAL_RULE_DAY);
recurBuf.append(";WKST=").append(day);
} else if (ruleEltName.equals(MailConstants.E_CAL_RULE_XNAME)) {
String name = ruleElt.getAttribute(MailConstants.A_CAL_RULE_XNAME_NAME, null);
if (name != null) {
String value = ruleElt.getAttribute(MailConstants.A_CAL_RULE_XNAME_VALUE, "");
// TODO: Escape/unescape value according to
// "text" rule.
recurBuf.append(";").append(name).append("=").append(value);
}
}
}
try {
ZRecur recur = new ZRecur(recurBuf.toString(), invTzMap);
if (exclude) {
subRules.add(new Recurrence.SimpleRepeatingRule(dtStart, dur, recur, null));
} else {
addRules.add(new Recurrence.SimpleRepeatingRule(dtStart, dur, recur, null));
}
} catch (ServiceException ex) {
throw ServiceException.INVALID_REQUEST("Exception parsing <recur> <rule>", ex);
}
} else {
throw ServiceException.INVALID_REQUEST("Expected <date> or <rule> inside of " + e.getName() + ", got " + intElt.getName(), null);
}
}
// iterate inside <add> or <exclude>
}
if (recurId != null) {
return new Recurrence.ExceptionRule(recurId, dtStart, dur, null, addRules, subRules);
} else {
return new Recurrence.RecurrenceRule(dtStart, dur, null, addRules, subRules);
}
}
use of com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence in project zm-mailbox by Zimbra.
the class CalendarItem method updateRecurrence.
private boolean updateRecurrence(long nextAlarm) throws ServiceException {
long startTime, endTime;
// update our recurrence rule, start with the initial rule
Invite firstInv = getDefaultInviteOrNull();
if (firstInv == null) {
return false;
}
IRecurrence recur = firstInv.getRecurrence();
if (recur instanceof Recurrence.RecurrenceRule) {
mRecurrence = (IRecurrence) recur.clone();
// now, go through the list of invites and find all the exceptions
for (Invite cur : mInvites) {
if (cur != firstInv) {
String method = cur.getMethod();
if (cur.isCancel()) {
assert (cur.hasRecurId());
if (cur.hasRecurId()) {
checkExdateIsSensible(cur.getRecurId());
Recurrence.CancellationRule cancelRule = new Recurrence.CancellationRule(cur.getRecurId());
((Recurrence.RecurrenceRule) mRecurrence).addException(cancelRule);
}
} else if (method.equals(ICalTok.REQUEST.toString()) || method.equals(ICalTok.PUBLISH.toString())) {
assert (cur.hasRecurId());
if (cur.hasRecurId() && cur.getStartTime() != null) {
checkRecurIdIsSensible(cur.getRecurId());
Recurrence.ExceptionRule exceptRule = null;
IRecurrence curRule = cur.getRecurrence();
if (curRule != null && curRule instanceof Recurrence.ExceptionRule) {
exceptRule = (Recurrence.ExceptionRule) curRule.clone();
} else {
// create a fake ExceptionRule wrapper around the single-instance
exceptRule = new Recurrence.ExceptionRule(cur.getRecurId(), cur.getStartTime(), cur.getEffectiveDuration(), new InviteInfo(cur));
}
((Recurrence.RecurrenceRule) mRecurrence).addException(exceptRule);
} else {
sLog.debug("Got second invite with no RecurID: " + cur.toString());
}
}
}
}
// Find the earliest DTSTART and latest DTEND. We're just looking for the bounds, so we won't worry
// about cancelled instances.
ParsedDateTime earliestStart = null;
ParsedDateTime latestEnd = null;
for (Invite cur : mInvites) {
if (!cur.isCancel()) {
ParsedDateTime start = cur.getStartTime();
if (earliestStart == null)
earliestStart = start;
else if (start != null && start.compareTo(earliestStart) < 0)
earliestStart = start;
ParsedDateTime end = cur.getEffectiveEndTime();
if (latestEnd == null)
latestEnd = end;
else if (end != null && end.compareTo(latestEnd) > 0)
latestEnd = end;
}
}
// Take the later of latestEnd and recurrence's end time.
ParsedDateTime recurEnd = mRecurrence.getEndTime();
if (latestEnd == null)
latestEnd = recurEnd;
else if (recurEnd != null && recurEnd.compareTo(latestEnd) > 0)
latestEnd = recurEnd;
// update the start and end time in the CalendarItem table if
// necessary
startTime = earliestStart != null ? earliestStart.getUtcTime() : 0;
endTime = latestEnd != null ? latestEnd.getUtcTime() : 0;
} else {
mRecurrence = null;
startTime = 0;
endTime = 0;
for (Invite inv : mInvites) {
if (!inv.isCancel()) {
ParsedDateTime dtStart = inv.getStartTime();
long st = dtStart != null ? dtStart.getUtcTime() : 0;
if (st != 0 && (st < startTime || startTime == 0))
startTime = st;
ParsedDateTime dtEnd = inv.getEffectiveEndTime();
long et = dtEnd != null ? dtEnd.getUtcTime() : 0;
if (et != 0 && et > endTime)
endTime = et;
}
}
}
// Adjust start/end times before recomputing alarm because alarm computation depends on those times.
boolean timesChanged = false;
if (mStartTime != startTime || mEndTime != endTime) {
timesChanged = true;
mStartTime = startTime;
mEndTime = endTime;
}
// Recompute next alarm. Bring appointment start time forward to the alarm time,
// if the next alarm is before the first instance.
recomputeNextAlarm(nextAlarm, false, false);
if (mAlarmData != null) {
long newNextAlarm = mAlarmData.getNextAtBase();
if (newNextAlarm > 0 && newNextAlarm < startTime && mStartTime != startTime) {
timesChanged = true;
mStartTime = newNextAlarm;
}
}
if (timesChanged) {
if (ZimbraLog.calendar.isDebugEnabled()) {
ZimbraLog.calendar.debug("Updating recurrence for %s. nextAlarm=%d.", getMailopContext(this), nextAlarm);
}
DbMailItem.updateInCalendarItemTable(this);
}
return true;
}
Aggregations