use of com.zimbra.cs.mailbox.CalendarItem in project zm-mailbox by Zimbra.
the class CalendarCollection method getAppointmentMap.
protected Map<String, DavResource> getAppointmentMap(DavContext ctxt, TimeRange range) throws ServiceException, DavException {
Mailbox mbox = getCalendarMailbox(ctxt);
HashMap<String, DavResource> appts = new HashMap<String, DavResource>();
ctxt.setCollectionPath(getUri());
if (range == null)
range = new TimeRange(getOwner());
long start = range.getStart();
long end = range.getEnd();
start = start == Long.MIN_VALUE ? -1 : start;
end = end == Long.MAX_VALUE ? -1 : end;
if (!needCalendarData(ctxt)) {
ZimbraLog.dav.debug("METADATA only");
mMetadataOnly = true;
for (CalendarItem.CalendarMetadata item : mbox.getCalendarItemMetadata(getId(), start, end)) {
appts.put(item.uid, new CalendarObject.LightWeightCalendarObject(getUri(), getOwner(), item));
}
} else {
for (CalendarItem calItem : mbox.getCalendarItemsForRange(ctxt.getOperationContext(), start, end, getId(), null)) appts.put(calItem.getUid(), new CalendarObject.LocalCalendarObject(ctxt, calItem));
}
return appts;
}
use of com.zimbra.cs.mailbox.CalendarItem in project zm-mailbox by Zimbra.
the class CalendarCollection method createItemFromInvites.
/**
* @param name Preferred DAV basename for the new item - including ".ics" if appropriate.
* @param allowUpdate - PUTs are allowed to update a pre-existing item. POSTs to the containing collection are not.
*/
public DavResource createItemFromInvites(DavContext ctxt, Account account, String name, List<Invite> invites, boolean allowUpdate) throws DavException, IOException {
boolean useEtag = allowUpdate;
try {
String user = account.getName();
/*
* Some of the CalDAV clients do not behave very well when it comes to etags.
* chandler doesn't set User-Agent header, doesn't understand If-None-Match or If-Match headers.
* evolution 2.8 always sets If-None-Match although we return etag in REPORT.
* ical correctly understands etag and sets If-Match for existing etags, but does not use If-None-Match
* for new resource creation.
*/
HttpServletRequest req = ctxt.getRequest();
String etag = null;
if (useEtag) {
etag = req.getHeader(DavProtocol.HEADER_IF_MATCH);
useEtag = (etag != null);
}
String baseName = HttpUtil.urlUnescape(name);
boolean acceptableClientChosenBasename = DebugConfig.enableDAVclientCanChooseResourceBaseName && baseName.equals(name);
if (name.endsWith(CalendarObject.CAL_EXTENSION)) {
name = name.substring(0, name.length() - CalendarObject.CAL_EXTENSION.length());
// Unescape the name (It was encoded in DavContext intentionally)
name = HttpUtil.urlUnescape(name);
}
String uid = findEventUid(invites);
Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(account);
CalendarItem origCalItem = null;
// Is the basename of the path client assigned rather than following the standard pattern?
Integer itemId = null;
if (acceptableClientChosenBasename) {
itemId = DavNames.get(this.mMailboxId, this.mId, baseName);
}
if (itemId != null) {
try {
MailItem mailItem = mbox.getItemById(ctxt.getOperationContext(), itemId, MailItem.Type.UNKNOWN);
if (mailItem != null && mailItem instanceof CalendarItem) {
origCalItem = (CalendarItem) mailItem;
}
} catch (ServiceException se) {
}
}
if (origCalItem == null) {
if (uid.equals(name)) {
origCalItem = mbox.getCalendarItemByUid(ctxt.getOperationContext(), name);
} else {
/* the basename of the path doesn't fit our preferred naming convention. */
origCalItem = mbox.getCalendarItemByUid(ctxt.getOperationContext(), uid);
String redirectUrl = null;
if (origCalItem != null) {
if (this.mId != origCalItem.getFolderId()) {
// In another folder, ignore
origCalItem = null;
} else {
// The item exists, but doesn't have this name - UID conflict.
if (acceptableClientChosenBasename) {
redirectUrl = hrefForCalendarItem(origCalItem, user, uid);
} else {
redirectUrl = defaultUrlForCalendarItem(user, uid);
}
throw new DavException.UidConflict("An item with the same UID already exists in the calendar", redirectUrl);
}
}
if ((origCalItem == null) && (!DebugConfig.enableDAVclientCanChooseResourceBaseName)) {
redirectUrl = defaultUrlForCalendarItem(user, uid);
}
if (allowUpdate && (redirectUrl != null)) {
/* SC_FOUND - Status code (302) indicating that the resource reside temporarily under a
* different URI. Since the redirection might be altered on occasion, the client should
* continue to use the Request-URI for future requests.(HTTP/1.1) To represent the status code
* (302), it is recommended to use this variable. Used to be called SC_MOVED_TEMPORARILY
*/
// sets status to SC_FOUND
ctxt.getResponse().sendRedirect(redirectUrl);
StringBuilder wrongUrlMsg = new StringBuilder();
wrongUrlMsg.append("wrong url - redirecting to:\n").append(redirectUrl);
throw new DavException(wrongUrlMsg.toString(), HttpServletResponse.SC_FOUND, null);
}
}
}
if (origCalItem == null && useEtag) {
throw new DavException("event not found", HttpServletResponse.SC_NOT_FOUND, null);
}
if (origCalItem != null && !allowUpdate) {
throw new DavException.UidConflict("An item with the same UID already exists in the calendar", hrefForCalendarItem(origCalItem, user, uid));
}
boolean isNewItem = true;
if (useEtag) {
String itemEtag = MailItemResource.getEtag(origCalItem);
if (!itemEtag.equals(etag)) {
throw new DavException(String.format("CalDAV client has stale event: event has different etag (%s) vs %s", itemEtag, etag), HttpServletResponse.SC_PRECONDITION_FAILED);
}
isNewItem = false;
}
// prepare to call Mailbox.setCalendarItem()
int flags = 0;
String[] tags = null;
List<ReplyInfo> replies = null;
Invite[] origInvites = null;
if (origCalItem != null) {
flags = origCalItem.getFlagBitmask();
tags = origCalItem.getTags();
replies = origCalItem.getAllReplies();
origInvites = origCalItem.getInvites();
}
SetCalendarItemData scidDefault = new SetCalendarItemData();
SetCalendarItemData[] scidExceptions = null;
int idxExceptions = 0;
boolean first = true;
for (Invite i : invites) {
// check for valid uid.
if (i.getUid() == null)
i.setUid(uid);
adjustOrganizer(ctxt, i);
// Carry over the MimeMessage/ParsedMessage to preserve any attachments.
// CalDAV clients don't support attachments, and on edit we have to either
// retain existing attachments or drop them. Retaining is better.
ParsedMessage oldPm = null;
if (origCalItem != null) {
Invite oldInv = origCalItem.getInvite(i.getRecurId());
if (oldInv == null && i.hasRecurId()) {
// It's a new exception instance. Inherit from series.
oldInv = origCalItem.getInvite((RecurId) null);
}
if (oldInv != null) {
MimeMessage mmInv = origCalItem.getSubpartMessage(oldInv.getMailItemId());
oldPm = mmInv != null ? new ParsedMessage(mmInv, false) : null;
}
}
if (first) {
scidDefault.invite = i;
scidDefault.message = oldPm;
first = false;
} else {
SetCalendarItemData scid = new SetCalendarItemData();
scid.invite = i;
scid.message = oldPm;
if (scidExceptions == null) {
scidExceptions = new SetCalendarItemData[invites.size() - 1];
}
scidExceptions[idxExceptions++] = scid;
}
// For attendee case, update replies list with matching ATTENDEE from the invite.
if (!i.isOrganizer() && replies != null) {
ZAttendee at = i.getMatchingAttendee(account);
if (at != null) {
AccountAddressMatcher acctMatcher = new AccountAddressMatcher(account);
ReplyInfo newReply = null;
for (Iterator<ReplyInfo> replyIter = replies.iterator(); replyIter.hasNext(); ) {
ReplyInfo reply = replyIter.next();
if (acctMatcher.matches(reply.getAttendee().getAddress())) {
RecurId ridR = reply.getRecurId(), ridI = i.getRecurId();
if ((ridR == null && ridI == null) || (ridR != null && ridR.equals(ridI))) {
// matching RECURRENCE-ID
// No need to compare SEQUENCE and DTSTAMP of existing reply and new invite.
// We're just going to take what the caldav client sent, even if it's older
// than the existing reply.
replyIter.remove();
if (!IcalXmlStrMap.PARTSTAT_NEEDS_ACTION.equalsIgnoreCase(at.getPartStat())) {
newReply = new ReplyInfo(at, i.getSeqNo(), i.getDTStamp(), ridI);
}
break;
}
}
}
if (newReply != null) {
replies.add(newReply);
}
}
}
}
CalendarItem newCalItem = null;
AutoScheduler autoScheduler = AutoScheduler.getAutoScheduler(mbox, this.getCalendarMailbox(ctxt), origInvites, mId, flags, tags, scidDefault, scidExceptions, replies, ctxt);
if (autoScheduler == null) {
newCalItem = mbox.setCalendarItem(ctxt.getOperationContext(), mId, flags, tags, scidDefault, scidExceptions, replies, CalendarItem.NEXT_ALARM_KEEP_CURRENT);
} else {
// Note: This also sets the calendar item
newCalItem = autoScheduler.doSchedulingActions();
}
if (newCalItem == null) {
throw new DavException("cannot create icalendar item - corrupt ICAL?", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
if (!uid.equals(name)) {
if (acceptableClientChosenBasename) {
DavNames.put(DavNames.DavName.create(this.mMailboxId, newCalItem.getFolderId(), baseName), newCalItem.getId());
}
}
return new CalendarObject.LocalCalendarObject(ctxt, newCalItem, isNewItem);
} catch (BadOrganizerException.DiffOrganizerInComponentsException e) {
throw new DavException.NeedSameOrganizerInAllComponents(e.getMessage());
} catch (BadOrganizerException e) {
// FORBIDDEN if we aren't going to be able to cope with the data
throw new DavException(e.getMessage(), HttpServletResponse.SC_FORBIDDEN, e);
} catch (ServiceException e) {
if (e.getCode().equals(ServiceException.FORBIDDEN)) {
throw new DavException(e.getMessage(), HttpServletResponse.SC_FORBIDDEN, e);
} else {
throw new DavException("cannot create icalendar item", HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
}
}
}
use of com.zimbra.cs.mailbox.CalendarItem in project zm-mailbox by Zimbra.
the class CalSummaryCache method reloadCalendarItemOverRange.
// mSummaryCache
//
// key = "accountId:folderId"
// value = CalendarData type
//
// CalendarData = {
// folderId, range start, range end,
// // list of CalendarItemData objects (one for each appointment in range)
// [
// CalendarItemData {
// calItemId, folderId, flags, tags, item type,
// last modified,
// actual range start, actual range end,
// uid, isRecurring, isPublic, alarm,
// default instance data (FullInstanceData type),
// // list of instances (FullInstanceData if an exception, InstanceData if not)
// [
// InstanceData/FullInstanceData, ...
// ]
// },
// CalendarItemData, ...
// ]
public static CalendarItemData reloadCalendarItemOverRange(CalendarItem calItem, long rangeStart, long rangeEnd) throws ServiceException {
CalendarItemData calItemData = null;
try {
boolean rangeValid = (rangeStart >= CalendarUtils.MICROSOFT_EPOC_START_MS_SINCE_EPOC && rangeEnd > CalendarUtils.MICROSOFT_EPOC_START_MS_SINCE_EPOC && rangeStart < rangeEnd);
if (!rangeValid) {
return null;
}
Invite defaultInvite = calItem.getDefaultInviteOrNull();
if (defaultInvite == null) {
ZimbraLog.calendar.info("Could not load defaultinfo for calendar item with id=" + calItem.getId() + "; SKIPPING");
return null;
}
String defaultFba = null;
if (calItem instanceof Appointment)
defaultFba = ((Appointment) calItem).getEffectiveFreeBusyActual(defaultInvite, null);
AlarmData alarm = null;
CalendarItem.AlarmData calItemAlarmData = calItem.getAlarmData();
long alarmTime = 0;
long alarmInst = 0;
if (calItemAlarmData != null) {
alarmTime = calItemAlarmData.getNextAt();
alarmInst = calItemAlarmData.getNextInstanceStart();
int alarmInvId = calItemAlarmData.getInvId();
int alarmCompNum = calItemAlarmData.getCompNum();
String summary = null, location = null;
Invite alarmInv = calItem.getInvite(alarmInvId, alarmCompNum);
if (alarmInv != null) {
summary = alarmInv.getName();
location = alarmInv.getLocation();
}
alarm = new AlarmData(calItemAlarmData.getNextAt(), calItemAlarmData.getNextInstanceStart(), alarmInvId, alarmCompNum, summary, location, calItemAlarmData.getAlarm());
}
Long defDtStartLong = null;
Long defDurationLong = null;
ParsedDateTime defDtStart = defaultInvite.getStartTime();
if (defDtStart != null) {
defDtStartLong = Long.valueOf(defDtStart.getUtcTime());
ParsedDateTime defDtEnd = defaultInvite.getEffectiveEndTime();
if (defDtEnd != null)
defDurationLong = Long.valueOf(defDtEnd.getUtcTime() - defDtStartLong.longValue());
}
String defaultEffectivePartStat = calItem.getEffectivePartStat(defaultInvite, null);
FullInstanceData defaultData = new FullInstanceData(defaultInvite, null, defDtStartLong, defDurationLong, defaultEffectivePartStat, defaultFba, null);
calItemData = new CalendarItemData(calItem.getType(), calItem.getFolderId(), calItem.getId(), calItem.getFlagString(), calItem.getTags(), TagUtil.getTagIdString(calItem), calItem.getModifiedSequence(), calItem.getSavedSequence(), calItem.getDate(), calItem.getChangeDate(), calItem.getSize(), defaultInvite.getUid(), defaultInvite.isRecurrence(), calItem.hasExceptions(), calItem.isPublic(), alarm, defaultData);
long actualRangeStart = 0;
long actualRangeEnd = 0;
int numInstances = 0;
Collection<CalendarItem.Instance> instances = calItem.expandInstances(rangeStart, rangeEnd, true);
for (CalendarItem.Instance inst : instances) {
try {
long instStart = inst.getStart();
long duration = inst.getEnd() - instStart;
// 0 means "no DTSTART", however, note that negative numbers are valid
Long instStartLong = instStart != 0 ? Long.valueOf(instStart) : null;
Long durationLong = duration > 0 ? Long.valueOf(duration) : null;
// 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;
boolean hasTimes = inst.hasStart() && inst.hasEnd();
if (hasTimes && (startOrAlarm >= rangeEnd || inst.getEnd() <= rangeStart)) {
continue;
}
numInstances++;
if (hasTimes) {
if (actualRangeStart == 0 || startOrAlarm < actualRangeStart)
actualRangeStart = startOrAlarm;
if (inst.getEnd() > actualRangeEnd)
actualRangeEnd = inst.getEnd();
}
InviteInfo invId = inst.getInviteInfo();
Invite inv = calItem.getInvite(invId.getMsgId(), invId.getComponentId());
Long alarmAt = instStart == alarmInst ? Long.valueOf(alarmTime) : null;
String fba = inv.getFreeBusyActual();
if (calItem instanceof Appointment)
fba = ((Appointment) calItem).getEffectiveFreeBusyActual(inv, inst);
String effectivePartStat = calItem.getEffectivePartStat(inv, inst);
InstanceData instData;
if (!inst.isException()) {
String ridZ = inst.getRecurIdZ();
Long tzOffset = instStartLong != null && inst.isAllDay() ? Long.valueOf(inst.getStartTzOffset()) : null;
instData = new InstanceData(ridZ, instStartLong, durationLong, alarmAt, tzOffset, effectivePartStat, fba, inv.getPercentComplete(), defaultData);
} else {
String ridZ = null;
if (inv.hasRecurId())
ridZ = inv.getRecurId().getDtZ();
instData = new FullInstanceData(inv, ridZ, instStartLong, durationLong, effectivePartStat, fba, alarmAt);
}
calItemData.addInstance(instData);
} catch (MailServiceException.NoSuchItemException e) {
ZimbraLog.calendar.info("Error could not get instance " + inst.getMailItemId() + "-" + inst.getComponentNum() + " for appt " + calItem.getId(), e);
}
}
if (numInstances < 1)
return null;
calItemData.setActualRange(actualRangeStart, actualRangeEnd);
} catch (MailServiceException.NoSuchItemException e) {
ZimbraLog.calendar.info("Error could not get default invite for calendar item: " + calItem.getId(), e);
} catch (RuntimeException e) {
ZimbraLog.calendar.info("Caught Exception " + e + " while getting summary info for calendar item: " + calItem.getId(), e);
}
return calItemData;
}
use of com.zimbra.cs.mailbox.CalendarItem in project zm-mailbox by Zimbra.
the class CalItemReminderService method notify.
@Override
public void notify(ChangeNotification notification) {
Account account = notification.mailboxAccount;
if (notification.mods.created != null) {
for (Map.Entry<ModificationKey, BaseItemInfo> entry : notification.mods.created.entrySet()) {
BaseItemInfo item = entry.getValue();
if (item instanceof CalendarItem) {
CalendarItem calItem = (CalendarItem) item;
ZimbraLog.scheduler.debug("Handling creation of calendar item (id=%s,mailboxId=%s)", calItem.getId(), calItem.getMailboxId());
scheduleNextReminders((CalendarItem) item, true, true);
}
}
}
if (notification.mods.modified != null) {
for (Map.Entry<ModificationKey, Change> entry : notification.mods.modified.entrySet()) {
Change change = entry.getValue();
if (change.what instanceof CalendarItem) {
CalendarItem calItem = (CalendarItem) change.what;
ZimbraLog.scheduler.debug("Handling modification of calendar item (id=%s,mailboxId=%s)", calItem.getId(), calItem.getMailboxId());
boolean calItemCanceled = false;
try {
if ((change.why & Change.FOLDER) != 0 && calItem.inTrash()) {
calItemCanceled = true;
}
} catch (ServiceException e) {
ZimbraLog.scheduler.error("Error in fetching calendar item's folder", e);
}
// cancel any existing reminders and schedule new ones if cal item not canceled
if (cancelExistingReminders(calItem) && !calItemCanceled)
scheduleNextReminders(calItem, true, true);
}
}
}
if (notification.mods.deleted != null) {
for (Map.Entry<ModificationKey, Change> entry : notification.mods.deleted.entrySet()) {
MailItem.Type type = (MailItem.Type) entry.getValue().what;
if (type == MailItem.Type.APPOINTMENT || type == MailItem.Type.TASK) {
Mailbox mbox = null;
try {
mbox = MailboxManager.getInstance().getMailboxByAccount(account, MailboxManager.FetchMode.DO_NOT_AUTOCREATE);
} catch (ServiceException e) {
ZimbraLog.scheduler.error("Error looking up the mailbox of account %s", account.getId(), e);
}
if (mbox != null) {
cancelExistingReminders(entry.getKey().getItemId(), mbox.getId());
}
}
}
}
}
use of com.zimbra.cs.mailbox.CalendarItem in project zm-mailbox by Zimbra.
the class CalItemReminderService method scheduleNextReminders.
/**
* Schedules next reminders for the calendar item.
*
* @param calItem
* @param email
* @param sms
*/
static void scheduleNextReminders(CalendarItem calItem, boolean email, boolean sms) {
try {
CalendarItem.AlarmData alarmData = calItem.getNextEmailAlarm();
if (alarmData == null)
return;
boolean emailAlarmExists = true;
boolean smsAlarmExists = false;
Alarm emailAlarm = alarmData.getAlarm();
List<ZAttendee> recipients = emailAlarm.getAttendees();
if (recipients != null && !recipients.isEmpty()) {
emailAlarmExists = false;
Account acct = calItem.getAccount();
String defaultEmailAddress = acct.getPrefCalendarReminderEmail();
String defaultDeviceAddress = acct.getCalendarReminderDeviceEmail();
for (ZAttendee recipient : recipients) {
if (recipient.getAddress().equals(defaultEmailAddress)) {
emailAlarmExists = true;
}
if (recipient.getAddress().equals(defaultDeviceAddress)) {
smsAlarmExists = true;
}
}
}
if (emailAlarmExists && email) {
scheduleReminder(new CalItemEmailReminderTask(), calItem, alarmData);
}
if (smsAlarmExists && sms) {
scheduleReminder(new CalItemSmsReminderTask(), calItem, alarmData);
}
} catch (ServiceException e) {
ZimbraLog.scheduler.error("Error in scheduling reminder task", e);
}
}
Aggregations