Search in sources :

Example 6 with ParsedDateTime

use of com.zimbra.common.calendar.ParsedDateTime in project zm-mailbox by Zimbra.

the class Appointment method checkAvailability.

private ConflictCheckResult checkAvailability(long now, Invite invite, int maxNumConflicts, int maxPctConflicts) throws ServiceException {
    long st, et;
    if (invite.isRecurrence()) {
        // Resource is getting invited to a recurring appointment.
        st = getStartTime();
        et = getEndTime();
    } else {
        // Resource is getting invited to a single instance.
        ParsedDateTime dtStart = invite.getStartTime();
        ParsedDateTime dtEnd = invite.getEffectiveEndTime();
        if (dtStart != null && dtEnd != null) {
            st = dtStart.getUtcTime();
            et = dtEnd.getUtcTime();
        } else {
            // Start time and/or end time can't be determined.  Give up.
            return null;
        }
    }
    // Ignore conflicts in the past.
    st = Math.max(st, now);
    if (et <= st)
        return null;
    OperationContext octxt = new OperationContext(getAccount());
    Collection<Instance> instances;
    if (invite.isRecurrence()) {
        instances = expandInstances(st, et, false);
    } else {
        instances = new ArrayList<Instance>(1);
        instances.add(Instance.fromInvite(getId(), invite));
    }
    if (instances == null || instances.isEmpty())
        return null;
    int maxByPct = maxPctConflicts * instances.size() / 100;
    int maxConflicts = Math.min(maxNumConflicts, maxByPct);
    List<Conflict> list = new ArrayList<Conflict>();
    int numConflicts = 0;
    boolean hasMoreConflicts = false;
    for (Instance inst : instances) {
        if (numConflicts > Math.max(maxConflicts, MIN_CONFLICT_LIST_SIZE - 1)) {
            hasMoreConflicts = true;
            break;
        }
        long start = inst.getStart();
        long end = inst.getEnd();
        // Run free/busy search of this user between instance start/end times.
        FreeBusy fb = getMailbox().getFreeBusy(octxt, start, end, this);
        String status = fb.getBusiest();
        if (Conflict.isBusy(status)) {
            list.add(new Conflict(inst, status, fb));
            numConflicts++;
        }
    }
    return new ConflictCheckResult(list, numConflicts > maxConflicts, hasMoreConflicts);
}
Also used : FreeBusy(com.zimbra.cs.fb.FreeBusy) FBInstance(com.zimbra.cs.fb.FreeBusy.FBInstance) ArrayList(java.util.ArrayList) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime)

Example 7 with ParsedDateTime

use of com.zimbra.common.calendar.ParsedDateTime in project zm-mailbox by Zimbra.

the class CalendarItem method create.

static CalendarItem create(int id, Folder folder, int flags, Tag.NormalizedTags ntags, String uid, ParsedMessage pm, Invite firstInvite, long nextAlarm, CustomMetadata custom) throws ServiceException {
    firstInvite.sanitize(false);
    if (!folder.canAccess(ACL.RIGHT_INSERT)) {
        throw ServiceException.PERM_DENIED("you do not have the required rights on the folder");
    }
    if (!firstInvite.isPublic() && !folder.canAccess(ACL.RIGHT_PRIVATE)) {
        throw ServiceException.PERM_DENIED("you do not have permission to create private calendar item in this folder");
    }
    Mailbox mbox = folder.getMailbox();
    if (pm != null && pm.hasAttachments()) {
        firstInvite.setHasAttachment(true);
        flags |= Flag.BITMASK_ATTACHED;
    } else {
        firstInvite.setHasAttachment(false);
        flags &= ~Flag.BITMASK_ATTACHED;
    }
    if (firstInvite.isDraft()) {
        flags |= Flag.BITMASK_DRAFT;
    } else {
        flags &= ~Flag.BITMASK_DRAFT;
    }
    if (firstInvite.isHighPriority()) {
        flags |= Flag.BITMASK_HIGH_PRIORITY;
    } else {
        flags &= ~Flag.BITMASK_HIGH_PRIORITY;
    }
    if (firstInvite.isLowPriority()) {
        flags |= Flag.BITMASK_LOW_PRIORITY;
    } else {
        flags &= ~Flag.BITMASK_LOW_PRIORITY;
    }
    MailItem.Type type = firstInvite.isEvent() ? Type.APPOINTMENT : Type.TASK;
    String sender = null;
    ZOrganizer org = firstInvite.getOrganizer();
    if (org != null) {
        sender = org.getIndexString();
    }
    sender = Strings.nullToEmpty(sender);
    String subject = Strings.nullToEmpty(firstInvite.getName());
    List<Invite> invites = new ArrayList<Invite>();
    invites.add(firstInvite);
    Recurrence.IRecurrence recur = firstInvite.getRecurrence();
    long startTime, endTime;
    if (recur != null) {
        ParsedDateTime dtStart = recur.getStartTime();
        startTime = dtStart != null ? dtStart.getUtcTime() : 0;
        ParsedDateTime dtEnd = recur.getEndTime();
        endTime = dtEnd != null ? dtEnd.getUtcTime() : 0;
    } else {
        ParsedDateTime dtStart = firstInvite.getStartTime();
        startTime = dtStart != null ? dtStart.getUtcTime() : 0;
        ParsedDateTime dtEnd = firstInvite.getEffectiveEndTime();
        endTime = dtEnd != null ? dtEnd.getUtcTime() : startTime;
    }
    Account account = mbox.getAccount();
    firstInvite.updateMyPartStat(account, firstInvite.getPartStat());
    UnderlyingData data = new UnderlyingData();
    data.id = id;
    data.type = type.toByte();
    data.folderId = folder.getId();
    if (!folder.inSpam() || mbox.getAccount().getBooleanAttr(Provisioning.A_zimbraJunkMessagesIndexingEnabled, false)) {
        data.indexId = IndexStatus.DEFERRED.id();
    }
    data.imapId = id;
    data.date = mbox.getOperationTimestamp();
    data.setFlags(flags & (Flag.FLAGS_CALITEM | Flag.FLAGS_GENERIC));
    data.setTags(ntags);
    data.setSubject(subject);
    data.metadata = encodeMetadata(DEFAULT_COLOR_RGB, 1, 1, custom, uid, startTime, endTime, recur, invites, firstInvite.getTimeZoneMap(), new ReplyList(), null);
    data.contentChanged(mbox, false);
    if (!firstInvite.hasRecurId()) {
        ZimbraLog.calendar.info("Adding CalendarItem: id=%d, Message-ID=\"%s\", folderId=%d, subject=\"%s\", UID=%s", data.id, pm != null ? pm.getMessageID() : "(none)", folder.getId(), firstInvite.isPublic() ? firstInvite.getName() : "(private)", firstInvite.getUid());
    } else {
        ZimbraLog.calendar.info("Adding CalendarItem: id=%d, Message-ID=\"%s\", folderId=%d, subject=\"%s\", UID=%s, recurId=%s", data.id, pm != null ? pm.getMessageID() : "(none)", folder.getId(), firstInvite.isPublic() ? firstInvite.getName() : "(private)", firstInvite.getUid(), firstInvite.getRecurId().getDtZ());
    }
    new DbMailItem(mbox).setSender(sender).create(data);
    CalendarItem item = type == Type.APPOINTMENT ? new Appointment(mbox, data) : new Task(mbox, data);
    Invite defInvite = item.getDefaultInviteOrNull();
    if (defInvite != null) {
        Collection<Instance> instances = item.expandInstances(CalendarUtils.MICROSOFT_EPOC_START_MS_SINCE_EPOC, Long.MAX_VALUE, false);
        if (instances.isEmpty()) {
            ZimbraLog.calendar.info("CalendarItem has effectively zero instances: id=%d, folderId=%d, subject=\"%s\", UID=%s ", data.id, folder.getId(), firstInvite.isPublic() ? firstInvite.getName() : "(private)", firstInvite.getUid());
            item.delete();
            throw ServiceException.FORBIDDEN("Recurring series has effectively zero instances");
        }
    }
    // If we're creating an invite during email delivery, always default to NEEDS_ACTION state.
    // If not email delivery, we assume the requesting client knows what it's doing and has set the
    // correct partstat in the invite.
    String defaultPartStat;
    if (mbox.getOperationContext() == null) {
        // octxt == null implies we're in email delivery.  (There needs to be better way to determine this...)
        defaultPartStat = IcalXmlStrMap.PARTSTAT_NEEDS_ACTION;
    } else {
        defaultPartStat = firstInvite.getPartStat();
    }
    item.processPartStat(firstInvite, pm != null ? pm.getMimeMessage() : null, true, defaultPartStat);
    item.finishCreation(null);
    folder.updateHighestMODSEQ();
    if (pm != null) {
        item.createBlob(pm, firstInvite);
    }
    item.mEndTime = item.recomputeRecurrenceEndTime(item.mEndTime);
    if (firstInvite.hasAlarm()) {
        item.recomputeNextAlarm(nextAlarm, false, false);
        item.saveMetadata();
        AlarmData alarmData = item.getAlarmData();
        if (alarmData != null) {
            long newNextAlarm = alarmData.getNextAtBase();
            if (newNextAlarm > 0 && newNextAlarm < item.mStartTime) {
                item.mStartTime = newNextAlarm;
            }
        }
    }
    DbMailItem.addToCalendarItemTable(item);
    Callback cb = getCallback();
    if (cb != null) {
        cb.created(item);
    }
    return item;
}
Also used : IRecurrence(com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence) Recurrence(com.zimbra.cs.mailbox.calendar.Recurrence) Account(com.zimbra.cs.account.Account) IRecurrence(com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence) DbMailItem(com.zimbra.cs.db.DbMailItem) ZOrganizer(com.zimbra.cs.mailbox.calendar.ZOrganizer) ArrayList(java.util.ArrayList) DbMailItem(com.zimbra.cs.db.DbMailItem) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) Invite(com.zimbra.cs.mailbox.calendar.Invite)

Example 8 with ParsedDateTime

use of com.zimbra.common.calendar.ParsedDateTime in project zm-mailbox by Zimbra.

the class CalendarItem method updateLocalExceptionsWhichMatchSeriesReply.

/**
     * Exceptions can be created which aren't communicated to ATTENDEEs, either because the ORGANIZER wants to
     * have local changes like a different alarm time or because a response is received which only affects one
     * instance of a series.
     * Caller is responsible for ensuring changed MetaData is written through to SQL sending notification of change.
     */
private void updateLocalExceptionsWhichMatchSeriesReply(Invite reply) throws ServiceException {
    if ((reply == null) || reply.getRecurId() != null) {
        // Only interested in series replies
        return;
    }
    IRecurrence replyRecurrence = reply.getRecurrence();
    if (replyRecurrence == null) {
        sLog.debug("Giving up on trying to match series reply to local exceptions - no recurrence in reply");
        return;
    }
    for (int i = 0; i < numInvites(); i++) {
        Invite cur = getInvite(i);
        if (!cur.classPropSetByMe() || (cur.getRecurId() == null)) {
            continue;
        }
        ParsedDateTime recurIdDT = cur.getRecurId().getDt();
        ParsedDateTime startDT = cur.getStartTime();
        // If the start time has moved then the series response can't be applicable.
        if ((recurIdDT == null) || (startDT == null) || !recurIdDT.sameTime(startDT)) {
            continue;
        }
        long utcTime = recurIdDT.getUtcTime();
        // Find instances within 2 seconds either side of start - assuming it will be a direct hit if found
        List<Instance> instances = Recurrence.expandInstances(replyRecurrence, getId(), utcTime - 2000L, utcTime + 2000L);
        if (instances == null || (instances.size() != 1)) {
            continue;
        }
        cur.updateMatchingAttendeesFromReply(reply);
    }
}
Also used : IRecurrence(com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) Invite(com.zimbra.cs.mailbox.calendar.Invite)

Example 9 with ParsedDateTime

use of com.zimbra.common.calendar.ParsedDateTime in project zm-mailbox by Zimbra.

the class CalendarItem method computeNextAlarms.

/**
     * Returns the next trigger times for alarms defined on the given Invite.
     * @param inv
     * @param lastAlarmsAt Map key is the 0-based alarm position and value is the last trigger time
     *                     for that alarm.
     * @return NextAlarms object that tells trigger time and instance start time by alarm position.
     *         It will not contain alarm position if that alarm doesn't have next trigger time later
     *         than its last trigger time.
     */
public NextAlarms computeNextAlarms(Invite inv, Map<Integer, Long> lastAlarmsAt) throws ServiceException {
    NextAlarms result = new NextAlarms();
    if (inv.getRecurrence() == null) {
        // non-recurring appointment or exception instance
        long instStart = 0, instEnd = 0;
        ParsedDateTime dtstart = inv.getStartTime();
        if (dtstart != null)
            instStart = dtstart.getUtcTime();
        ParsedDateTime dtend = inv.getEffectiveEndTime();
        if (dtend != null)
            instEnd = dtend.getUtcTime();
        List<Alarm> alarms = inv.getAlarms();
        int index = 0;
        for (Iterator<Alarm> iter = alarms.iterator(); iter.hasNext(); index++) {
            Alarm alarm = iter.next();
            Long lastAtLong = lastAlarmsAt.get(index);
            if (lastAtLong != null) {
                long lastAt = lastAtLong.longValue();
                long triggerAt = alarm.getTriggerTime(instStart, instEnd);
                if (lastAt < triggerAt)
                    result.add(index, triggerAt, instStart);
            }
        }
    } else {
        // series invite of recurring appointment
        long oldest;
        oldest = Long.MAX_VALUE;
        for (long lastAt : lastAlarmsAt.values()) {
            oldest = Math.min(oldest, lastAt);
        }
        long endTime = getNextAlarmRecurrenceExpansionLimit();
        Collection<Instance> instances = expandInstances(oldest, endTime, false);
        List<Alarm> alarms = inv.getAlarms();
        int index = 0;
        for (Iterator<Alarm> iter = alarms.iterator(); iter.hasNext(); index++) {
            Alarm alarm = iter.next();
            Long lastAtLong = lastAlarmsAt.get(index);
            if (lastAtLong != null) {
                long lastAt = lastAtLong.longValue();
                for (Instance inst : instances) {
                    if (// only look at non-exception instances
                    inst.isException())
                        continue;
                    long instStart = inst.getStart();
                    if (instStart < lastAt && inst.hasStart())
                        continue;
                    long triggerAt = alarm.getTriggerTime(instStart, inst.getEnd());
                    if (lastAt < triggerAt) {
                        result.add(index, triggerAt, instStart);
                        // We can break now because we know alarms on later instances are even later.
                        break;
                    }
                }
            }
        }
    }
    return result;
}
Also used : Alarm(com.zimbra.cs.mailbox.calendar.Alarm) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime)

Example 10 with ParsedDateTime

use of com.zimbra.common.calendar.ParsedDateTime in project zm-mailbox by Zimbra.

the class CalendarRequest method inviteIsAfterTime.

// Check if invite is relevant after the given time.  Invite is relevant if its DTSTART
// or RECURRENCE-ID comes after the reference time.  For all-day appointment, look back
// 24 hours to account for possible TZ difference.
protected static boolean inviteIsAfterTime(Invite inv, long time) {
    long startUtc = 0;
    ParsedDateTime dtStart = inv.getStartTime();
    if (dtStart != null)
        startUtc = dtStart.getUtcTime();
    long ridUtc = 0;
    RecurId rid = inv.getRecurId();
    if (rid != null) {
        ParsedDateTime ridDt = rid.getDt();
        if (ridDt != null)
            ridUtc = ridDt.getUtcTime();
    }
    long invTime = Math.max(startUtc, ridUtc);
    if (inv.isAllDayEvent())
        time -= MSECS_PER_DAY;
    return invTime >= time;
}
Also used : ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) RecurId(com.zimbra.cs.mailbox.calendar.RecurId)

Aggregations

ParsedDateTime (com.zimbra.common.calendar.ParsedDateTime)66 ParsedDuration (com.zimbra.common.calendar.ParsedDuration)23 Invite (com.zimbra.cs.mailbox.calendar.Invite)20 ICalTimeZone (com.zimbra.common.calendar.ICalTimeZone)18 ParseException (java.text.ParseException)16 ArrayList (java.util.ArrayList)16 IRecurrence (com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence)15 ZProperty (com.zimbra.common.calendar.ZCalendar.ZProperty)14 Element (com.zimbra.common.soap.Element)13 RecurId (com.zimbra.cs.mailbox.calendar.RecurId)11 TimeZoneMap (com.zimbra.common.calendar.TimeZoneMap)10 Account (com.zimbra.cs.account.Account)10 ZOrganizer (com.zimbra.cs.mailbox.calendar.ZOrganizer)10 ZVCalendar (com.zimbra.common.calendar.ZCalendar.ZVCalendar)9 ICalTok (com.zimbra.common.calendar.ZCalendar.ICalTok)8 ZAttendee (com.zimbra.cs.mailbox.calendar.ZAttendee)8 ZComponent (com.zimbra.common.calendar.ZCalendar.ZComponent)7 ZParameter (com.zimbra.common.calendar.ZCalendar.ZParameter)7 ServiceException (com.zimbra.common.service.ServiceException)7 CalendarItem (com.zimbra.cs.mailbox.CalendarItem)7