Пример #1
0
/**
* Return a RepeatRuleDateRange from the earliest start to the latest end of the event.
* 
* @todo: This should probably be made part of the VCalendar object when we move the RRule.php into AWL.
*
* @param object $vResource A vComponent which is a VCALENDAR containing components needing expansion
* @return RepeatRuleDateRange Representing the range of time covered by the event. 
*/
function getVCalendarRange($vResource)
{
    global $c;
    $components = $vResource->GetComponents();
    $dtstart = null;
    $duration = null;
    $earliest_start = null;
    $latest_end = null;
    $has_repeats = false;
    foreach ($components as $k => $comp) {
        if ($comp->GetType() == 'VTIMEZONE') {
            continue;
        }
        $range = getComponentRange($comp);
        $dtstart = $range->from;
        if (!isset($dtstart)) {
            continue;
        }
        $duration = $range->getDuration();
        $rrule = $comp->GetProperty('RRULE');
        $limited_occurrences = true;
        if (isset($rrule)) {
            $rule = new RepeatRule($dtstart, $rrule);
            $limited_occurrences = $rule->hasLimitedOccurrences();
        }
        if ($limited_occurrences) {
            $instances = array();
            $instances[$dtstart->FloatOrUTC()] = $dtstart;
            if (!isset($range_end)) {
                $range_end = new RepeatRuleDateTime();
                $range_end->modify('+150 years');
            }
            $instances += rrule_expand($dtstart, 'RRULE', $comp, $range_end);
            $instances += rdate_expand($dtstart, 'RDATE', $comp, $range_end);
            foreach (rdate_expand($dtstart, 'EXDATE', $comp, $range_end) as $k => $v) {
                unset($instances[$k]);
            }
            if (count($instances) < 1) {
                if (empty($earliest_start) || $dtstart < $earliest_start) {
                    $earliest_start = $dtstart;
                }
                $latest_end = null;
                break;
            }
            $instances = array_keys($instances);
            asort($instances);
            $first = new RepeatRuleDateTime($instances[0]);
            $last = new RepeatRuleDateTime($instances[count($instances) - 1]);
            $last->modify($duration);
            if (empty($earliest_start) || $first < $earliest_start) {
                $earliest_start = $first;
            }
            if (empty($latest_end) || $last > $latest_end) {
                $latest_end = $last;
            }
        } else {
            if (empty($earliest_start) || $dtstart < $earliest_start) {
                $earliest_start = $dtstart;
            }
            $latest_end = null;
            break;
        }
    }
    return new RepeatRuleDateRange($earliest_start, $latest_end);
}
Пример #2
0
/**
* Expand the event instances for an iCalendar VEVENT (or VTODO)
* 
* Note: expansion here does not apply modifications to instances other than modifying start/end/due/duration.
*
* @param object $vResource A vComponent which is a VCALENDAR containing components needing expansion
* @param object $range_start A RepeatRuleDateTime which is the beginning of the range for events, default -6 weeks
* @param object $range_end A RepeatRuleDateTime which is the end of the range for events, default +6 weeks
*
* @return vComponent The original vComponent, with the instances of the internal components expanded.
*/
function expand_event_instances($vResource, $range_start = null, $range_end = null)
{
    $components = $vResource->GetComponents();
    if (!isset($range_start)) {
        $range_start = new RepeatRuleDateTime();
        $range_start->modify('-6 weeks');
    }
    if (!isset($range_end)) {
        $range_end = clone $range_start;
        $range_end->modify('+6 months');
    }
    $new_components = array();
    $result_limit = 1000;
    $instances = array();
    $expand = false;
    $dtstart = null;
    foreach ($components as $k => $comp) {
        if ($comp->GetType() != 'VEVENT' && $comp->GetType() != 'VTODO' && $comp->GetType() != 'VJOURNAL') {
            if ($comp->GetType() != 'VTIMEZONE') {
                $new_components[] = $comp;
            }
            continue;
        }
        if (!isset($dtstart)) {
            $dtstart = $comp->GetProperty('DTSTART');
            $dtstart = new RepeatRuleDateTime($dtstart);
            $instances[$dtstart->UTC()] = $comp;
        }
        $p = $comp->GetProperty('RECURRENCE-ID');
        if (isset($p) && $p->Value() != '') {
            $range = $p->GetParameterValue('RANGE');
            $recur_utc = new RepeatRuleDateTime($p);
            $recur_utc = $recur_utc->UTC();
            if (isset($range) && $range == 'THISANDFUTURE') {
                foreach ($instances as $k => $v) {
                    if (DEBUG_RRULE) {
                        printf("Removing overridden instance at: {$k}\n");
                    }
                    if ($k >= $recur_utc) {
                        unset($instances[$k]);
                    }
                }
            } else {
                unset($instances[$recur_utc]);
            }
        } else {
            if (DEBUG_RRULE) {
                $p = $comp->GetProperty('SUMMARY');
                $summary = isset($p) ? $p->Value() : 'not set';
                $p = $comp->GetProperty('UID');
                $uid = isset($p) ? $p->Value() : 'not set';
                printf("Processing event '%s' with UID '%s' starting on %s\n", $summary, $uid, $dtstart->UTC());
                print "Instances at start";
                foreach ($instances as $k => $v) {
                    print ' : ' . $k;
                }
                print "\n";
            }
        }
        $instances = array_merge($instances, rrule_expand($dtstart, 'RRULE', $comp, $range_end));
        if (DEBUG_RRULE) {
            print "After rrule_expand";
            foreach ($instances as $k => $v) {
                print ' : ' . $k;
            }
            print "\n";
        }
        $instances = array_merge($instances, rdate_expand($dtstart, 'RDATE', $comp, $range_end));
        if (DEBUG_RRULE) {
            print "After rdate_expand";
            foreach ($instances as $k => $v) {
                print ' : ' . $k;
            }
            print "\n";
        }
        foreach (rdate_expand($dtstart, 'EXDATE', $comp, $range_end) as $k => $v) {
            unset($instances[$k]);
        }
        if (DEBUG_RRULE) {
            print "After exdate_expand";
            foreach ($instances as $k => $v) {
                print ' : ' . $k;
            }
            print "\n";
        }
    }
    $last_duration = null;
    $early_start = null;
    $new_components = array();
    $start_utc = $range_start->UTC();
    $end_utc = $range_end->UTC();
    foreach ($instances as $utc => $comp) {
        if ($utc > $end_utc) {
            break;
        }
        $end_type = $comp->GetType() == 'VTODO' ? 'DUE' : 'DTEND';
        $duration = $comp->GetProperty('DURATION');
        if (!isset($duration) || $duration->Value() == '') {
            $instance_start = $comp->GetProperty('DTSTART');
            $dtsrt = new RepeatRuleDateTime($instance_start);
            $instance_end = $comp->GetProperty($end_type);
            if (isset($instance_end)) {
                $dtend = new RepeatRuleDateTime($instance_end);
                $duration = $dtstart->RFC5545Duration($dtend);
            } else {
                if ($instance_start->GetParameterValue('VALUE') == 'DATE') {
                    $duration = 'P1D';
                } else {
                    $duration = 'P0D';
                    // For clarity
                }
            }
        } else {
            $duration = $duration->Value();
        }
        if ($utc < $start_utc) {
            if (isset($early_start) && isset($last_duration) && $duration == $last_duration) {
                if ($utc < $early_start) {
                    continue;
                }
            } else {
                /** Calculate the latest possible start date when this event would overlap our range start */
                $latest_start = clone $range_start;
                $latest_start->modify('-' . $duration);
                $early_start = $latest_start->UTC();
                $last_duration = $duration;
                if ($utc < $early_start) {
                    continue;
                }
            }
        }
        $component = clone $comp;
        $component->ClearProperties(array('DTSTART' => true, 'DUE' => true, 'DTEND' => true));
        $component->AddProperty('DTSTART', $utc);
        $component->AddProperty('DURATION', $duration);
        $new_components[] = $component;
    }
    $vResource->SetComponents($new_components);
    return $vResource;
}
Пример #3
0
 $expanded->MaskComponents(array('VEVENT' => 1, 'VTODO' => 1, 'VJOURNAL' => 1));
 $instances = $expanded->GetComponents();
 $trigger = new vProperty($alarm->trigger);
 $related = $trigger->GetParameterValue('RELATED');
 $first = new RepeatRuleDateTime($alarm->dtstart);
 $first->modify($trigger->Value());
 $next = null;
 $last = null;
 foreach ($instances as $k => $component) {
     $when = new RepeatRuleDateTime($component->GetPValue('DTSTART'));
     // a UTC value
     if ($args->debug) {
         printf("refresh: Looking at event instance on '%s'\n", $when->UTC());
     }
     if ($related == 'END') {
         $when->modify($component->GetPValue('DURATION'));
     }
     $when->modify($trigger->Value());
     if ($when > $expand_range_start && $when < $expand_range_end && (!isset($next) || $when < $next)) {
         $next = clone $when;
     }
     if ($args->set_last && (!isset($last) || $when > $last)) {
         $last = clone $when;
     }
 }
 $trigger_type = $trigger->GetParameterValue('VALUE');
 if ($trigger_type == 'DATE' || $trigger_type == 'DATE-TIME' || preg_match('{^\\d{8}T\\d{6}Z?$}', $trigger->Value())) {
     $first = new RepeatRuleDateTime($trigger);
     if ($first > $expand_range_start && (empty($next) || $first < $next)) {
         $next = $first;
     } else {
Пример #4
0
/**
* Expand the instances for a STANDARD or DAYLIGHT component of a VTIMEZONE 
*
* @param object $vResource is a VCALENDAR with a VTIMEZONE containing components needing expansion
* @param object $range_start A RepeatRuleDateTime which is the beginning of the range for events.
* @param object $range_end A RepeatRuleDateTime which is the end of the range for events.
* @param int $offset_from The offset from UTC in seconds at the onset time.
*
* @return array of onset datetimes with UTC from/to offsets
*/
function expand_timezone_onsets(vCalendar $vResource, RepeatRuleDateTime $range_start, RepeatRuleDateTime $range_end)
{
    global $c;
    $vtimezones = $vResource->GetComponents();
    $vtz = $vtimezones[0];
    $components = $vtz->GetComponents();
    $instances = array();
    $dtstart = null;
    $is_date = false;
    $has_repeats = false;
    $zone_tz = $vtz->GetPValue('TZID');
    foreach ($components as $k => $comp) {
        if (DEBUG_EXPAND) {
            printf("Starting TZ expansion for component '%s' in timezone '%s'\n", $comp->GetType(), $zone_tz);
            foreach ($instances as $k => $v) {
                print ' : ' . $k;
            }
            print "\n";
        }
        $dtstart_prop = $comp->GetProperty('DTSTART');
        if (!isset($dtstart_prop)) {
            continue;
        }
        $dtstart = new RepeatRuleDateTime($dtstart_prop);
        $dtstart->setTimeZone('UTC');
        $offset_from = $comp->GetPValue('TZOFFSETFROM');
        $offset_from = $offset_from / 100 * 3600 + abs($offset_from) % 100 * 60 * ($offset_from < 0 ? -1 : 0);
        $offset_from *= -1;
        $offset_from = "{$offset_from} seconds";
        dbg_error_log('tz/update', "%s of offset\n", $offset_from);
        $dtstart->modify($offset_from);
        $is_date = $dtstart->isDate();
        $instances[$dtstart->UTC('Y-m-d\\TH:i:s\\Z')] = $comp;
        $rrule = $comp->GetProperty('RRULE');
        $has_repeats = isset($rrule);
        if (!$has_repeats) {
            continue;
        }
        $recur = $comp->GetProperty('RRULE');
        if (isset($recur)) {
            $recur = $recur->Value();
            $this_start = clone $dtstart;
            $rule = new RepeatRule($this_start, $recur, $is_date);
            $i = 0;
            $result_limit = 1000;
            while ($date = $rule->next()) {
                $instances[$date->UTC('Y-m-d\\TH:i:s\\Z')] = $comp;
                if ($i++ >= $result_limit || $date > $range_end) {
                    break;
                }
            }
            if (DEBUG_EXPAND) {
                print "After rrule_expand";
                foreach ($instances as $k => $v) {
                    print ' : ' . $k;
                }
                print "\n";
            }
        }
        $properties = $comp->GetProperties('RDATE');
        if (count($properties)) {
            foreach ($properties as $p) {
                $timezone = $p->GetParameterValue('TZID');
                $rdate = $p->Value();
                $rdates = explode(',', $rdate);
                foreach ($rdates as $k => $v) {
                    $rdate = new RepeatRuleDateTime($v, $timezone, $is_date);
                    if ($return_floating_times) {
                        $rdate->setAsFloat();
                    }
                    $instances[$rdate->UTC('Y-m-d\\TH:i:s\\Z')] = $comp;
                    if ($rdate > $range_end) {
                        break;
                    }
                }
            }
            if (DEBUG_EXPAND) {
                print "After rdate_expand";
                foreach ($instances as $k => $v) {
                    print ' : ' . $k;
                }
                print "\n";
            }
        }
    }
    ksort($instances);
    $onsets = array();
    $start_utc = $range_start->UTC('Y-m-d\\TH:i:s\\Z');
    $end_utc = $range_end->UTC('Y-m-d\\TH:i:s\\Z');
    foreach ($instances as $utc => $comp) {
        if ($utc > $end_utc) {
            if (DEBUG_EXPAND) {
                printf("We're done: {$utc} is out of the range.\n");
            }
            break;
        }
        if ($utc < $start_utc) {
            continue;
        }
        $onsets[$utc] = array('from' => $comp->GetPValue('TZOFFSETFROM'), 'to' => $comp->GetPValue('TZOFFSETTO'), 'name' => $comp->GetPValue('TZNAME'), 'type' => $comp->GetType());
    }
    return $onsets;
}
Пример #5
0
require_once 'RRule-v2.php';
require_once 'vCalendar.php';
/**
* Essentially what we are doing is:
*
DELETE FROM caldav_data JOIN calendar_item USING(dav_id)
      WHERE dav_name LIKE '/someprincipal/%'
        AND (
          (rrule IS NULL AND dtend < archive_before_date)
           OR (last_instance_end < archive_before_date)
        )
*/
$recent = new RepeatRuleDateTime(gmdate('Ymd\\THis\\Z'));
$recent->modify('P-6D');
$archive_before_date = new RepeatRuleDateTime(gmdate('Ymd\\THis\\Z'));
$archive_before_date->modify($args->older);
if ($archive_before_date > $recent) {
    echo "Cowardly refusing to archive events before " + $archive_before_date->format('Y-m-d H:i:s') + "\n";
}
if ($args->debug) {
    printf("Archiving event instances finished before '%s'\n", $archive_before_date->UTC());
}
// SQL to create an archive collection but only if it doesn't exist already.
$archive_collection_sql = <<<EOSQL
INSERT INTO collection (user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar,
                        created, modified, public_events_only, publicly_readable, is_addressbook,
                        resourcetypes, schedule_transp, timezone, description)
     SELECT user_no, parent_container, :archive_dav_name, random(),
            'Archive of ' || dav_displayname, true, current_timestamp, current_timestamp, false, false, false,
            resourcetypes, schedule_transp, timezone, 
            'Archive of ' || CASE WHEN description IS NULL OR description = '' THEN dav_name ELSE description END