use of com.zimbra.common.calendar.ParsedDuration 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.common.calendar.ParsedDuration in project zm-mailbox by Zimbra.
the class WorkingHours method getRecurrenceForDay.
private static IRecurrence getRecurrenceForDay(int dayOfWeek, StartSpec startSpec, TimeRange timeRange, ICalTimeZone tz, TimeZoneMap tzmap) throws ServiceException {
// DTSTART
String dateStr = startSpec.getDateString(dayOfWeek);
String dtStartStr;
if (tz.sameAsUTC())
dtStartStr = String.format("%sT%02d%02d00Z", dateStr, timeRange.start.hour, timeRange.start.minute);
else
dtStartStr = String.format("TZID=\"%s\":%sT%02d%02d00", tz.getID(), dateStr, timeRange.start.hour, timeRange.start.minute);
ParsedDateTime dtStart;
try {
dtStart = ParsedDateTime.parse(dtStartStr, tzmap);
} catch (ParseException e) {
throw ServiceException.INVALID_REQUEST("Bad date/time value \"" + dtStartStr + "\"", e);
}
// DURATION
ParsedDuration dur = timeRange.getDuration();
// RRULE
String dayName = DayOfWeekName.lookup(dayOfWeek);
String ruleStr = String.format("FREQ=WEEKLY;INTERVAL=1;BYDAY=%s", dayName);
return new Recurrence.SimpleRepeatingRule(dtStart, dur, new ZRecur(ruleStr, tzmap), null);
}
use of com.zimbra.common.calendar.ParsedDuration in project zm-mailbox by Zimbra.
the class GetCalendarItemSummaries method encodeCalendarItemInstances.
/**
* Encodes a calendar item
*
* @param parent
* @param elementName
* name of element to add (MailConstants .E_APPOINTMENT or MailConstants.E_TASK)
* @param rangeStart
* start period to expand instances (or -1 for no start time constraint)
* @param rangeEnd
* end period to expand instances (or -1 for no end time constraint)
* @param newFormat
* temporary HACK - true: SearchRequest, false: GetAppointmentSummaries
* @return
*/
static EncodeCalendarItemResult encodeCalendarItemInstances(ZimbraSoapContext lc, OperationContext octxt, CalendarItem calItem, Account acct, long rangeStart, long rangeEnd, boolean newFormat) throws ServiceException {
EncodeCalendarItemResult toRet = new EncodeCalendarItemResult();
ItemIdFormatter ifmt = new ItemIdFormatter(lc);
Account authAccount = getAuthenticatedAccount(lc);
boolean hidePrivate = !calItem.allowPrivateAccess(authAccount, lc.isUsingAdminPrivileges());
try {
boolean expandRanges;
if (calItem instanceof Task) {
expandRanges = true;
if (rangeStart == -1 && rangeEnd == -1) {
rangeStart = Long.MIN_VALUE;
rangeEnd = Long.MAX_VALUE;
}
} else {
expandRanges = (rangeStart != -1 && rangeEnd != -1 && rangeStart < rangeEnd);
}
boolean isAppointment = calItem instanceof Appointment;
// Use the marshalling code in calendar summary cache for uniform output, when we can.
if (isAppointment && expandRanges) {
CalendarItemData calItemData = CalSummaryCache.reloadCalendarItemOverRange(calItem, rangeStart, rangeEnd);
if (calItemData != null) {
int numInstances = calItemData.getNumInstances();
if (numInstances > 0) {
Element calItemElem = CacheToXML.encodeCalendarItemData(lc, ifmt, calItemData, !hidePrivate, !newFormat);
toRet.element = calItemElem;
toRet.numInstancesExpanded = numInstances;
}
}
return toRet;
}
// But there are other cases (e.g. tasks, no time range) that require the legacy code below.
// don't initialize until we find at least one valid instance
Element calItemElem = null;
Invite defaultInvite = calItem.getDefaultInviteOrNull();
if (defaultInvite == null) {
mLog.info("Could not load defaultinfo for calendar item with id=" + calItem.getId() + " SKIPPING");
return toRet;
}
ParsedDuration defDuration = defaultInvite.getEffectiveDuration();
// events and 1 second for non all-day. (bug 28615)
if (defDuration == null && !defaultInvite.isTodo()) {
if (defaultInvite.isAllDayEvent()) {
defDuration = ParsedDuration.ONE_DAY;
} else {
defDuration = ParsedDuration.ONE_SECOND;
}
}
long defDurationMsecs = 0;
if (defaultInvite.getStartTime() != null && defDuration != null) {
ParsedDateTime s = defaultInvite.getStartTime();
long et = s.add(defDuration).getUtcTime();
defDurationMsecs = et - s.getUtcTime();
}
String defaultFba = null;
if (calItem instanceof Appointment) {
defaultFba = ((Appointment) calItem).getEffectiveFreeBusyActual(defaultInvite, null);
}
String defaultPtSt = calItem.getEffectivePartStat(defaultInvite, null);
AlarmData alarmData = calItem.getAlarmData();
// add all the instances:
int numInRange = 0;
if (expandRanges) {
Collection<CalendarItem.Instance> instances = calItem.expandInstances(rangeStart, rangeEnd, true);
long alarmTime = 0;
long alarmInst = 0;
if (alarmData != null) {
alarmTime = alarmData.getNextAt();
alarmInst = alarmData.getNextInstanceStart();
}
for (CalendarItem.Instance inst : instances) {
try {
InviteInfo invId = inst.getInviteInfo();
Invite inv = calItem.getInvite(invId.getMsgId(), invId.getComponentId());
boolean showAll = !hidePrivate || inv.isPublic();
// figure out which fields are different from the default and put their data here...
ParsedDuration invDuration = inv.getEffectiveDuration();
long instStart = inst.getStart();
// For an instance whose alarm time is within the time range, we must
// include it even if its start time is after the range.
long startOrAlarm = instStart == alarmInst ? alarmTime : instStart;
// events and 1 second for non all-day. (bug 28615)
if (invDuration == null) {
if (inv.isAllDayEvent())
invDuration = ParsedDuration.ONE_DAY;
else
invDuration = ParsedDuration.ONE_SECOND;
}
if (!inst.hasStart() || (startOrAlarm < rangeEnd && invDuration.addToTime(instStart) > rangeStart)) {
numInRange++;
} else {
continue;
}
if (calItemElem == null) {
calItemElem = lc.createElement(isAppointment ? MailConstants.E_APPOINTMENT : MailConstants.E_TASK);
if (showAll) {
// flags and tags
ToXML.recordItemTags(calItemElem, calItem, octxt);
}
// Organizer
if (inv.hasOrganizer()) {
ZOrganizer org = inv.getOrganizer();
org.toXml(calItemElem);
}
calItemElem.addAttribute("x_uid", calItem.getUid());
calItemElem.addAttribute(MailConstants.A_UID, calItem.getUid());
}
Element instElt = calItemElem.addElement(MailConstants.E_INSTANCE);
if (showAll) {
if (isAppointment && inv.isEvent()) {
String instFba = ((Appointment) calItem).getEffectiveFreeBusyActual(inv, inst);
if (instFba != null && (!instFba.equals(defaultFba) || inst.isException()))
instElt.addAttribute(MailConstants.A_APPT_FREEBUSY_ACTUAL, instFba);
}
String instPtSt = calItem.getEffectivePartStat(inv, inst);
if (!defaultPtSt.equals(instPtSt) || inst.isException())
instElt.addAttribute(MailConstants.A_CAL_PARTSTAT, instPtSt);
}
if (inst.hasStart()) {
instElt.addAttribute(MailConstants.A_CAL_START_TIME, instStart);
if (inv.isAllDayEvent())
instElt.addAttribute(MailConstants.A_CAL_TZ_OFFSET, inst.getStartTzOffset());
}
if (inst.isException() && inv.hasRecurId()) {
RecurId rid = inv.getRecurId();
instElt.addAttribute(MailConstants.A_CAL_RECURRENCE_ID_Z, rid.getDtZ());
} else {
instElt.addAttribute(MailConstants.A_CAL_RECURRENCE_ID_Z, inst.getRecurIdZ());
}
if (inst.isException()) {
instElt.addAttribute(MailConstants.A_CAL_IS_EXCEPTION, true);
instElt.addAttribute(MailConstants.A_CAL_INV_ID, ifmt.formatItemId(calItem, inst.getMailItemId()));
instElt.addAttribute(MailConstants.A_CAL_COMPONENT_NUM, inst.getComponentNum());
if (showAll) {
// fragment has already been sanitized...
String frag = inv.getFragment();
if (frag != null && !frag.equals(""))
instElt.addAttribute(MailConstants.E_FRAG, frag, Element.Disposition.CONTENT);
if (inv.getPriority() != null)
instElt.addAttribute(MailConstants.A_CAL_PRIORITY, inv.getPriority());
if (inv.isEvent()) {
if (inv.getFreeBusy() != null)
instElt.addAttribute(MailConstants.A_APPT_FREEBUSY, inv.getFreeBusy());
if (inv.getTransparency() != null)
instElt.addAttribute(MailConstants.A_APPT_TRANSPARENCY, inv.getTransparency());
}
if (inv.isTodo()) {
if (inv.getPercentComplete() != null)
instElt.addAttribute(MailConstants.A_TASK_PERCENT_COMPLETE, inv.getPercentComplete());
}
if (inv.getName() != null)
instElt.addAttribute(MailConstants.A_NAME, inv.getName());
if (inv.getLocation() != null)
instElt.addAttribute(MailConstants.A_CAL_LOCATION, inv.getLocation());
List<String> categories = inv.getCategories();
if (categories != null) {
for (String cat : categories) {
instElt.addElement(MailConstants.E_CAL_CATEGORY).setText(cat);
}
}
Geo geo = inv.getGeo();
if (geo != null)
geo.toXml(instElt);
if (inv.hasOtherAttendees())
instElt.addAttribute(MailConstants.A_CAL_OTHER_ATTENDEES, true);
if (inv.hasAlarm())
instElt.addAttribute(MailConstants.A_CAL_ALARM, true);
}
instElt.addAttribute(MailConstants.A_CAL_ISORG, inv.isOrganizer());
if (inv.isTodo()) {
if (inst.hasEnd()) {
instElt.addAttribute(MailConstants.A_TASK_DUE_DATE, inst.getEnd());
if (inv.isAllDayEvent())
instElt.addAttribute(MailConstants.A_CAL_TZ_OFFSET_DUE, inst.getEndTzOffset());
}
} else {
if (inst.hasStart() && inst.hasEnd()) {
instElt.addAttribute(newFormat ? MailConstants.A_CAL_NEW_DURATION : MailConstants.A_CAL_DURATION, inst.getEnd() - inst.getStart());
}
}
if (inv.getStatus() != null)
instElt.addAttribute(MailConstants.A_CAL_STATUS, inv.getStatus());
if (inv.getClassProp() != null)
instElt.addAttribute(MailConstants.A_CAL_CLASS, inv.getClassProp());
if (inv.isAllDayEvent())
instElt.addAttribute(MailConstants.A_CAL_ALLDAY, true);
if (inv.isDraft())
instElt.addAttribute(MailConstants.A_CAL_DRAFT, true);
if (inv.isNeverSent())
instElt.addAttribute(MailConstants.A_CAL_NEVER_SENT, true);
if (inv.isRecurrence())
instElt.addAttribute(MailConstants.A_CAL_RECUR, true);
} else {
if (inv.isTodo()) {
if (inst.hasEnd()) {
instElt.addAttribute(MailConstants.A_TASK_DUE_DATE, inst.getEnd());
if (inv.isAllDayEvent())
instElt.addAttribute(MailConstants.A_CAL_TZ_OFFSET_DUE, inst.getEndTzOffset());
}
} else {
// the default duration due to daylight savings time transitions.
if (inst.hasStart() && inst.hasEnd() && defDurationMsecs != inst.getEnd() - inst.getStart()) {
instElt.addAttribute(newFormat ? MailConstants.A_CAL_NEW_DURATION : MailConstants.A_CAL_DURATION, inst.getEnd() - inst.getStart());
}
}
}
} catch (MailServiceException.NoSuchItemException e) {
mLog.info("Error could not get instance " + inst.getMailItemId() + "-" + inst.getComponentNum() + " for appt " + calItem.getId(), e);
}
}
// iterate all the instances
}
if (!expandRanges || numInRange > 0) {
// if we found any calItems at all, we have to encode the "Default" data here
boolean showAll = !hidePrivate || defaultInvite.isPublic();
if (calItemElem == null) {
calItemElem = lc.createElement(isAppointment ? MailConstants.E_APPOINTMENT : MailConstants.E_TASK);
calItemElem.addAttribute("x_uid", calItem.getUid());
calItemElem.addAttribute(MailConstants.A_UID, calItem.getUid());
if (showAll) {
// flags and tags
ToXML.recordItemTags(calItemElem, calItem, octxt);
}
// Organizer
if (defaultInvite.hasOrganizer()) {
ZOrganizer org = defaultInvite.getOrganizer();
org.toXml(calItemElem);
}
}
if (showAll) {
if (alarmData != null)
ToXML.encodeAlarmData(calItemElem, calItem, alarmData);
String defaultPriority = defaultInvite.getPriority();
if (defaultPriority != null)
calItemElem.addAttribute(MailConstants.A_CAL_PRIORITY, defaultPriority);
calItemElem.addAttribute(MailConstants.A_CAL_PARTSTAT, defaultPtSt);
if (defaultInvite.isEvent()) {
calItemElem.addAttribute(MailConstants.A_APPT_FREEBUSY, defaultInvite.getFreeBusy());
calItemElem.addAttribute(MailConstants.A_APPT_FREEBUSY_ACTUAL, defaultFba);
calItemElem.addAttribute(MailConstants.A_APPT_TRANSPARENCY, defaultInvite.getTransparency());
}
if (defaultInvite.isTodo()) {
String pctComplete = defaultInvite.getPercentComplete();
if (pctComplete != null)
calItemElem.addAttribute(MailConstants.A_TASK_PERCENT_COMPLETE, pctComplete);
}
calItemElem.addAttribute(MailConstants.A_NAME, defaultInvite.getName());
calItemElem.addAttribute(MailConstants.A_CAL_LOCATION, defaultInvite.getLocation());
List<String> categories = defaultInvite.getCategories();
if (categories != null) {
for (String cat : categories) {
calItemElem.addElement(MailConstants.E_CAL_CATEGORY).setText(cat);
}
}
Geo geo = defaultInvite.getGeo();
if (geo != null)
geo.toXml(calItemElem);
// fragment has already been sanitized...
String fragment = defaultInvite.getFragment();
if (!fragment.equals(""))
calItemElem.addAttribute(MailConstants.E_FRAG, fragment, Element.Disposition.CONTENT);
if (defaultInvite.hasOtherAttendees()) {
calItemElem.addAttribute(MailConstants.A_CAL_OTHER_ATTENDEES, defaultInvite.hasOtherAttendees());
}
if (defaultInvite.hasAlarm()) {
calItemElem.addAttribute(MailConstants.A_CAL_ALARM, defaultInvite.hasAlarm());
}
}
calItemElem.addAttribute(MailConstants.A_CAL_ISORG, defaultInvite.isOrganizer());
calItemElem.addAttribute(MailConstants.A_ID, ifmt.formatItemId(calItem));
calItemElem.addAttribute(MailConstants.A_CAL_INV_ID, ifmt.formatItemId(calItem, defaultInvite.getMailItemId()));
calItemElem.addAttribute(MailConstants.A_CAL_COMPONENT_NUM, defaultInvite.getComponentNum());
calItemElem.addAttribute(MailConstants.A_FOLDER, ifmt.formatItemId(new ItemId(calItem.getMailbox().getAccountId(), calItem.getFolderId())));
calItemElem.addAttribute(MailConstants.A_CAL_STATUS, defaultInvite.getStatus());
calItemElem.addAttribute(MailConstants.A_CAL_CLASS, defaultInvite.getClassProp());
if (!defaultInvite.isTodo())
calItemElem.addAttribute(newFormat ? MailConstants.A_CAL_NEW_DURATION : MailConstants.A_CAL_DURATION, defDurationMsecs);
if (defaultInvite.isAllDayEvent())
calItemElem.addAttribute(MailConstants.A_CAL_ALLDAY, defaultInvite.isAllDayEvent());
if (defaultInvite.isDraft())
calItemElem.addAttribute(MailConstants.A_CAL_DRAFT, defaultInvite.isDraft());
if (defaultInvite.isNeverSent())
calItemElem.addAttribute(MailConstants.A_CAL_NEVER_SENT, defaultInvite.isNeverSent());
if (defaultInvite.isRecurrence())
calItemElem.addAttribute(MailConstants.A_CAL_RECUR, defaultInvite.isRecurrence());
if (calItem.hasExceptions()) {
calItemElem.addAttribute(MailConstants.A_CAL_HAS_EXCEPTIONS, true);
}
toRet.element = calItemElem;
}
toRet.numInstancesExpanded = numInRange;
} catch (MailServiceException.NoSuchItemException e) {
mLog.info("Error could not get default invite for calendar item: " + calItem.getId(), e);
} catch (RuntimeException e) {
mLog.info("Caught Exception " + e + " while getting summary info for calendar item: " + calItem.getId(), e);
}
return toRet;
}
use of com.zimbra.common.calendar.ParsedDuration in project zm-mailbox by Zimbra.
the class CalendarUtils method parseInviteElementCommon.
/**
* UID, DTSTAMP, and SEQUENCE **MUST** be set by caller
*
* @param account
* user receiving invite
* @param element
* invite XML element
* @param newInv
* Invite we are currently building up
* @param oldTzMap
* time zone map from A DIFFERENT invite; if this method is
* called during modify operation, this map contains time zones
* before the modification; null if called during create
* operation
* @return
* @throws ServiceException
*/
private static void parseInviteElementCommon(Account account, MailItem.Type type, Element element, Invite newInv, boolean recurrenceIdAllowed, boolean recurAllowed) throws ServiceException {
//zdsync
String invId = element.getAttribute(MailConstants.A_ID, null);
Element compElem = element.getOptionalElement(MailConstants.E_INVITE_COMPONENT);
if (compElem != null) {
element = compElem;
}
//zdsync
String dts = element.getAttribute(MailConstants.A_CAL_DATETIME, null);
TimeZoneMap tzMap = newInv.getTimeZoneMap();
parseTimeZones(element.getParent(), tzMap);
newInv.setItemType(type);
// UID
String uid = element.getAttribute(MailConstants.A_UID, null);
if (uid != null && uid.length() > 0)
newInv.setUid(uid);
// RECURRENCE-ID
if (recurrenceIdAllowed) {
Element e = element.getOptionalElement(MailConstants.E_CAL_EXCEPTION_ID);
if (e != null) {
ParsedDateTime dt = parseDateTime(e, tzMap);
RecurId recurId = new RecurId(dt, RecurId.RANGE_NONE);
newInv.setRecurId(recurId);
}
} else {
if (element.getOptionalElement(MailConstants.E_CAL_EXCEPTION_ID) != null) {
throw ServiceException.INVALID_REQUEST("May not specify an <exceptId> in this request", null);
}
}
String name = element.getAttribute(MailConstants.A_NAME, "");
String location = element.getAttribute(MailConstants.A_CAL_LOCATION, "");
// CATEGORIES
for (Iterator<Element> catIter = element.elementIterator(MailConstants.E_CAL_CATEGORY); catIter.hasNext(); ) {
String cat = catIter.next().getText();
newInv.addCategory(cat);
}
// COMMENTs
for (Iterator<Element> cmtIter = element.elementIterator(MailConstants.E_CAL_COMMENT); cmtIter.hasNext(); ) {
String cmt = cmtIter.next().getText();
newInv.addComment(cmt);
}
// CONTACTs
for (Iterator<Element> cnIter = element.elementIterator(MailConstants.E_CAL_CONTACT); cnIter.hasNext(); ) {
String contact = cnIter.next().getTextTrim();
newInv.addContact(contact);
}
// GEO
Element geoElem = element.getOptionalElement(MailConstants.E_CAL_GEO);
if (geoElem != null) {
Geo geo = Geo.parse(geoElem);
newInv.setGeo(geo);
}
// URL
String url = element.getAttribute(MailConstants.A_CAL_URL, null);
newInv.setUrl(url);
// SEQUENCE
int seq = (int) element.getAttributeLong(MailConstants.A_CAL_SEQUENCE, 0);
newInv.setSeqNo(seq);
// SUMMARY (aka Name or Subject)
newInv.setName(name);
// DESCRIPTION
Element descElem = element.getOptionalElement(MailConstants.E_CAL_DESCRIPTION);
String desc = descElem != null ? descElem.getText() : null;
Element descHtmlElem = element.getOptionalElement(MailConstants.E_CAL_DESC_HTML);
String descHtml = descHtmlElem != null ? descHtmlElem.getText() : null;
newInv.setDescription(desc, descHtml);
boolean allDay = element.getAttributeBool(MailConstants.A_CAL_ALLDAY, false);
newInv.setIsAllDayEvent(allDay);
// DTSTART
Element startElem;
if (newInv.isTodo())
startElem = element.getOptionalElement(MailConstants.E_CAL_START_TIME);
else
startElem = element.getElement(MailConstants.E_CAL_START_TIME);
if (startElem != null) {
ParsedDateTime dt = parseDtElement(startElem, tzMap, newInv);
// fixup for bug 30121
if (allDay && dt.hasTime()) {
// If this is supposed to be an all-day event but DTSTART has time part, clear the time part.
dt.setHasTime(false);
} else if (!allDay && !dt.hasTime()) {
// If the event isn't marked as all-day but DTSTART is date-only, the client simply forgot
// to mark it all-day. Do all-day implicitly.
allDay = true;
newInv.setIsAllDayEvent(allDay);
}
newInv.setDtStart(dt);
}
// DTEND (for VEVENT) or DUE (for VTODO)
Element endElem = element.getOptionalElement(MailConstants.E_CAL_END_TIME);
if (endElem != null) {
ParsedDateTime dt = parseDtElement(endElem, tzMap, newInv);
// fixup for bug 30121
if (allDay && dt.hasTime()) {
// If this is supposed to be an all-day event but DTEND has time part, clear the time part.
dt.setHasTime(false);
} else if (!allDay && !dt.hasTime()) {
// If the event isn't marked as all-day but DTEND is date-only, the client simply forgot
// to mark it all-day. Do all-day implicitly.
allDay = true;
newInv.setIsAllDayEvent(allDay);
}
if (allDay && !newInv.isTodo()) {
// HACK ALERT: okay, campers, here's the deal.
// By definition, our end dates are EXCLUSIVE: DTEND is not
// included.. eg a meeting 7-8pm actually stops at 7:59
//
// This makes sense for normal appointments, but apparently
// this rule is confusing to people when making
// all-day-events
//
// For all-day-events, people want to say that a 1-day-long
// appointment starts on 11/1 and ends on 11/1, for example
// this is inconsistent (and incompatible with RFC2445) but
// it is what people want. Sooo, we to a bit of a hacky
// translation when sending/receiving all-day-events.
//
dt = dt.add(ParsedDuration.ONE_DAY);
}
newInv.setDtEnd(dt);
} else {
// DURATION
Element d = element.getOptionalElement(MailConstants.E_CAL_DURATION);
if (d != null) {
ParsedDuration pd = ParsedDuration.parse(d);
newInv.setDuration(pd);
}
}
// LOCATION
newInv.setLocation(location);
// STATUS
String status = element.getAttribute(MailConstants.A_CAL_STATUS, newInv.isEvent() ? IcalXmlStrMap.STATUS_CONFIRMED : IcalXmlStrMap.STATUS_NEEDS_ACTION);
validateAttr(IcalXmlStrMap.sStatusMap, MailConstants.A_CAL_STATUS, status);
newInv.setStatus(status);
// CLASS
String classProp = element.getAttribute(MailConstants.A_CAL_CLASS, IcalXmlStrMap.CLASS_PUBLIC);
validateAttr(IcalXmlStrMap.sClassMap, MailConstants.A_CAL_CLASS, classProp);
newInv.setClassProp(classProp);
// PRIORITY
String priority = element.getAttribute(MailConstants.A_CAL_PRIORITY, null);
newInv.setPriority(priority);
if (newInv.isEvent()) {
// FreeBusy
String fb = element.getAttribute(MailConstants.A_APPT_FREEBUSY, null);
if (fb != null) {
newInv.setFreeBusy(fb);
// Intended F/B takes precedence over TRANSP.
if (IcalXmlStrMap.FBTYPE_FREE.equals(fb))
newInv.setTransparency(IcalXmlStrMap.TRANSP_TRANSPARENT);
else
newInv.setTransparency(IcalXmlStrMap.TRANSP_OPAQUE);
} else {
// TRANSP is examined only when intended F/B is not supplied.
String transp = element.getAttribute(MailConstants.A_APPT_TRANSPARENCY, IcalXmlStrMap.TRANSP_OPAQUE);
validateAttr(IcalXmlStrMap.sTranspMap, MailConstants.A_APPT_TRANSPARENCY, transp);
newInv.setTransparency(transp);
// If opaque, don't set intended f/b because there are multiple possibilities.
if (newInv.isTransparent())
newInv.setFreeBusy(IcalXmlStrMap.FBTYPE_FREE);
}
}
if (newInv.isTodo()) {
// PERCENT-COMPLETE
String pctComplete = element.getAttribute(MailConstants.A_TASK_PERCENT_COMPLETE, null);
newInv.setPercentComplete(pctComplete);
// COMPLETED
String completed = element.getAttribute(MailConstants.A_TASK_COMPLETED, null);
if (completed != null) {
try {
ParsedDateTime c = ParsedDateTime.parseUtcOnly(completed);
newInv.setCompleted(c.getUtcTime());
} catch (ParseException e) {
throw ServiceException.INVALID_REQUEST("Invalid COMPLETED value: " + completed, e);
}
} else if (status.equals(IcalXmlStrMap.STATUS_COMPLETED)) {
newInv.setCompleted(System.currentTimeMillis());
} else {
newInv.setCompleted(0);
}
}
// ATTENDEEs
boolean hasAttendees = false;
for (Iterator<Element> iter = element.elementIterator(MailConstants.E_CAL_ATTENDEE); iter.hasNext(); ) {
ZAttendee at = ZAttendee.parse(iter.next());
newInv.addAttendee(at);
hasAttendees = true;
}
if (hasAttendees && newInv.getMethod().equals(ICalTok.PUBLISH.toString())) {
newInv.setMethod(ICalTok.REQUEST.toString());
}
// ORGANIZER
Element orgElt = element.getOptionalElement(MailConstants.E_CAL_ORGANIZER);
if (orgElt != null) {
ZOrganizer org = ZOrganizer.parse(orgElt);
newInv.setOrganizer(org);
}
// Once we have organizer and attendee information, we can tell if this account is the
// organizer in this invite or not.
newInv.setIsOrganizer(account);
if (!newInv.isCancel()) {
// draft flag
// True means invite has changes that haven't been sent to attendees.
boolean draft = element.getAttributeBool(MailConstants.A_CAL_DRAFT, false);
newInv.setDraft(draft);
// neverSent flag
// True means attendees have never been notified for this invite.
boolean neverSent = element.getAttributeBool(MailConstants.A_CAL_NEVER_SENT, false);
newInv.setNeverSent(neverSent);
}
// RECUR
Element recur = element.getOptionalElement(MailConstants.A_CAL_RECUR);
if (recur != null) {
if (!recurAllowed) {
throw ServiceException.INVALID_REQUEST("No <recur> allowed in an exception", null);
}
// Ensure DTSTART is set if doing recurrence.
ParsedDateTime st = newInv.getStartTime();
if (st == null) {
ParsedDateTime et = newInv.getEndTime();
if (et != null) {
if (et.hasTime())
st = et.add(ParsedDuration.NEGATIVE_ONE_SECOND);
else
st = et.add(ParsedDuration.NEGATIVE_ONE_DAY);
newInv.setDtStart(st);
} else {
// Both DTSTART and DTEND are unspecified. Recurrence makes no sense!
throw ServiceException.INVALID_REQUEST("recurrence used without DTSTART", null);
}
}
Recurrence.IRecurrence recurrence = parseRecur(recur, tzMap, newInv.getStartTime(), newInv.getEndTime(), newInv.getDuration(), newInv.getRecurId());
newInv.setRecurrence(recurrence);
}
// VALARMs
Iterator<Element> alarmsIter = element.elementIterator(MailConstants.E_CAL_ALARM);
while (alarmsIter.hasNext()) {
Alarm alarm = Alarm.parse(alarmsIter.next());
if (alarm != null)
newInv.addAlarm(alarm);
}
List<ZProperty> xprops = parseXProps(element);
for (ZProperty prop : xprops) newInv.addXProp(prop);
newInv.validateDuration();
//zdsync: must set this only after recur is processed
if (invId != null) {
try {
int invIdInt = Integer.parseInt(invId);
newInv.setInviteId(invIdInt);
} catch (NumberFormatException e) {
// ignore if invId is not a number, e.g. refers to a remote account
}
}
if (dts != null) {
newInv.setDtStamp(Long.parseLong(dts));
}
Element fragment = element.getOptionalElement(MailConstants.E_FRAG);
if (fragment != null) {
newInv.setFragment(fragment.getText());
}
}
use of com.zimbra.common.calendar.ParsedDuration 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);
}
}
Aggregations