mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-03-15 08:20:12 +00:00
Refactoring free/busy handling to a single core routine with RRule-2
This commit is contained in:
parent
64f20edaab
commit
fa67ef987e
105
inc/RRule-v2.php
105
inc/RRule-v2.php
@ -37,42 +37,34 @@ $GLOBALS['debug_rrule'] = false;
|
||||
* Wrap the DateTimeZone class to allow parsing some iCalendar TZID strangenesses
|
||||
*/
|
||||
class RepeatRuleTimeZone extends DateTimeZone {
|
||||
private $tzid;
|
||||
private $tz_defined;
|
||||
|
||||
public function __construct($dtz = null) {
|
||||
$this->tzid = false;
|
||||
$this->tz_defined = false;
|
||||
if ( !isset($dtz) ) return;
|
||||
|
||||
$dtz = olson_from_tzstring($dtz);
|
||||
|
||||
try {
|
||||
parent::__construct($dtz);
|
||||
$this->tzid = $dtz;
|
||||
$this->tz_defined = $dtz;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$original = $dtz;
|
||||
$dtz = olson_from_tzstring($dtz);
|
||||
if ( isset($dtz) ) {
|
||||
try {
|
||||
parent::__construct($dtz);
|
||||
$this->tzid = $dtz;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
dbg_error_log( 'ERROR', 'Could not parse timezone "%s" - will use floating time', $original );
|
||||
$dtz = new DateTimeZone('UTC');
|
||||
$this->tzid = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if ( !isset($dtz) ) {
|
||||
dbg_error_log( 'ERROR', 'Could not parse timezone "%s" - will use floating time', $original );
|
||||
$dtz = new DateTimeZone('UTC');
|
||||
$this->tzid = false;
|
||||
$this->tz_defined = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tzid() {
|
||||
$tzid = parent::getName();
|
||||
if ( $this->tz_defined === false ) return false;
|
||||
$tzid = $this->getName();
|
||||
if ( $tzid != 'UTC' ) return $tzid;
|
||||
return $this->tzid;
|
||||
return $this->tz_defined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +83,8 @@ class RepeatRuleDateTime extends DateTime {
|
||||
|
||||
public function __construct($date = null, $dtz = null) {
|
||||
$this->is_date = false;
|
||||
if ( !isset($date) ) return;
|
||||
|
||||
if ( preg_match('{;?VALUE=DATE[:;]}', $date, $matches) ) $this->is_date = true;
|
||||
elseif ( preg_match('{:([12]\d{3}) (0[1-9]|1[012]) (0[1-9]|[12]\d|3[01]Z?) $}x', $date, $matches) ) $this->is_date = true;
|
||||
if (preg_match('/;?TZID=([^:]+).*:(\d{8}(T\d{6})?)(Z)?/', $date, $matches) ) {
|
||||
@ -150,7 +144,8 @@ class RepeatRuleDateTime extends DateTime {
|
||||
if ( isset($matches[6]) && $matches[6] != '' ) $interval .= $minus . $matches[7] . ' minutes ';
|
||||
if ( isset($matches[8]) && $matches[8] != '' ) $interval .= $minus . $matches[9] . ' seconds ';
|
||||
}
|
||||
return (string)parent::modify($interval);
|
||||
parent::modify($interval);
|
||||
return $this->__toString();
|
||||
}
|
||||
|
||||
|
||||
@ -184,6 +179,11 @@ class RepeatRuleDateTime extends DateTime {
|
||||
}
|
||||
|
||||
|
||||
public function RFC5545Duration( $end_stamp ) {
|
||||
return sprintf( 'PT%dM', intval(($end_stamp->epoch() - $this->epoch()) / 60) );
|
||||
}
|
||||
|
||||
|
||||
public function setTimeZone( $tz ) {
|
||||
if ( is_string($tz) ) {
|
||||
$tz = new RepeatRuleTimeZone($tz);
|
||||
@ -655,13 +655,13 @@ class RepeatRule {
|
||||
}
|
||||
|
||||
|
||||
require_once("iCalendar.php");
|
||||
require_once("vComponent.php");
|
||||
|
||||
/**
|
||||
* Expand the event instances for an RDATE or EXDATE property
|
||||
*
|
||||
* @param string $property RDATE or EXDATE, depending...
|
||||
* @param array $component An iCalComponent which is applies for these instances
|
||||
* @param array $component A vComponent which is a VEVENT, VTODO or VJOURNAL
|
||||
* @param array $range_end A date after which we care less about expansion
|
||||
*
|
||||
* @return array An array keyed on the UTC dates, referring to the component
|
||||
@ -688,7 +688,7 @@ function rdate_expand( $dtstart, $property, $component, $range_end = null ) {
|
||||
*
|
||||
* @param object $dtstart A RepeatRuleDateTime which is the master dtstart
|
||||
* @param string $property RDATE or EXDATE, depending...
|
||||
* @param array $component An iCalComponent which is applies for these instances
|
||||
* @param array $component A vComponent which is a VEVENT, VTODO or VJOURNAL
|
||||
* @param array $range_end A date after which we care less about expansion
|
||||
*
|
||||
* @return array An array keyed on the UTC dates, referring to the component
|
||||
@ -696,13 +696,14 @@ function rdate_expand( $dtstart, $property, $component, $range_end = null ) {
|
||||
function rrule_expand( $dtstart, $property, $component, $range_end ) {
|
||||
$expansion = array();
|
||||
|
||||
$recur = $component->GetPValue($property);
|
||||
$recur = $component->GetProperty($property);
|
||||
if ( !isset($recur) ) return $expansion;
|
||||
$recur = $recur->Value();
|
||||
|
||||
$this_start = $component->GetPValue('DTSTART');
|
||||
$this_start = $component->GetProperty('DTSTART');
|
||||
if ( isset($this_start) ) {
|
||||
$timezone = $component->GetPParamValue('DTSTART', 'TZID');
|
||||
$this_start = new RepeatRuleDateTime($this_start,$timezone);
|
||||
$timezone = $this_start->GetParameterValue('TZID');
|
||||
$this_start = new RepeatRuleDateTime($this_start->Value(),$timezone);
|
||||
}
|
||||
else {
|
||||
$this_start = clone($dtstart);
|
||||
@ -722,14 +723,14 @@ function rrule_expand( $dtstart, $property, $component, $range_end ) {
|
||||
/**
|
||||
* Expand the event instances for an iCalendar VEVENT (or VTODO)
|
||||
*
|
||||
* @param object $ics An iCalComponent which is the master VCALENDAR
|
||||
* @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 object $ics 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 iCalComponent The original iCalComponent with expanded events in the range.
|
||||
* @return vComponent The original vComponent, with the instances of the internal components expanded.
|
||||
*/
|
||||
function expand_event_instances( $ics, $range_start = null, $range_end = null ) {
|
||||
$components = $ics->GetComponents();
|
||||
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'); }
|
||||
@ -745,15 +746,16 @@ function expand_event_instances( $ics, $range_start = null, $range_end = null )
|
||||
continue;
|
||||
}
|
||||
if ( !isset($dtstart) ) {
|
||||
$tzid = $comp->GetPParamValue('DTSTART', 'TZID');
|
||||
$dtstart = new RepeatRuleDateTime( $comp->GetPValue('DTSTART'), $tzid );
|
||||
$dtstart = $comp->GetProperty('DTSTART');
|
||||
$tzid = $dtstart->GetParameterValue('TZID');
|
||||
$dtstart = new RepeatRuleDateTime( $dtstart->Value(), $tzid );
|
||||
$instances[$dtstart->UTC()] = $comp;
|
||||
}
|
||||
$p = $comp->GetPValue('RECURRENCE-ID');
|
||||
if ( isset($p) && $p != '' ) {
|
||||
$range = $comp->GetPParamValue('RECURRENCE-ID', 'RANGE');
|
||||
$recur_tzid = $comp->GetPParamValue('RECURRENCE-ID', 'TZID');
|
||||
$recur_utc = new RepeatRuleDateTime($p,$recur_tzid);
|
||||
$p = $comp->GetProperty('RECURRENCE-ID');
|
||||
if ( isset($p) && $p->Value() != '' ) {
|
||||
$range = $p->GetParameterValue('RANGE');
|
||||
$recur_tzid = $p->GetParameterValue('TZID');
|
||||
$recur_utc = new RepeatRuleDateTime($p->Value(),$recur_tzid);
|
||||
$recur_utc = $recur_utc->UTC();
|
||||
if ( isset($range) && $range == 'THISANDFUTURE' ) {
|
||||
foreach( $instances AS $k => $v ) {
|
||||
@ -781,12 +783,16 @@ function expand_event_instances( $ics, $range_start = null, $range_end = null )
|
||||
if ( $utc > $end_utc ) break;
|
||||
|
||||
$end_type = ($comp->GetType() == 'VTODO' ? 'DUE' : 'DTEND');
|
||||
$duration = $comp->GetPValue('DURATION');
|
||||
$duration = $comp->GetProperty('DURATION');
|
||||
if ( !isset($duration) ) {
|
||||
if ( !isset($end) ) $end = $comp->GetPValue('DUE');
|
||||
$dtend = new RepeatRuleDateTime( $comp->GetPValue($end_type), $comp->GetPParamValue($end_type, 'TZID'));
|
||||
$dtsrt = new RepeatRuleDateTime( $comp->GetPValue('DTSTART'), $comp->GetPParamValue('DTSTART', 'TZID'));
|
||||
$duration = sprintf( 'PT%dM', intval(($dtend->epoch() - $dtsrt->epoch()) / 60) );
|
||||
$instance_start = $comp->GetProperty('DTSTART');
|
||||
$dtsrt = new RepeatRuleDateTime( $instance_start->Value(), $instance_start->GetParameterValue('TZID'));
|
||||
$instance_end = $comp->GetProperty($end_type);
|
||||
$dtend = new RepeatRuleDateTime( $instance_end->Value(), $instance_end->GetParameterValue('TZID'));
|
||||
$duration = $dtstart->RFC5545Duration( $dtend );
|
||||
}
|
||||
else {
|
||||
$duration = $duration->Value();
|
||||
}
|
||||
|
||||
if ( $utc < $start_utc ) {
|
||||
@ -802,8 +808,7 @@ function expand_event_instances( $ics, $range_start = null, $range_end = null )
|
||||
}
|
||||
}
|
||||
$component = clone($comp);
|
||||
$component->ClearProperties('DTSTART');
|
||||
$component->ClearProperties($end_type);
|
||||
$component->ClearProperties( array('DTSTART'=> true, 'DUE' => true, 'DTEND' => true) );
|
||||
$component->AddProperty('DTSTART', $utc );
|
||||
$component->AddProperty('DURATION', $duration );
|
||||
$new_components[] = $component;
|
||||
@ -811,11 +816,11 @@ function expand_event_instances( $ics, $range_start = null, $range_end = null )
|
||||
}
|
||||
|
||||
if ( $in_range ) {
|
||||
$ics->SetComponents($new_components);
|
||||
$vResource->SetComponents($new_components);
|
||||
}
|
||||
else {
|
||||
$ics->SetComponents(array());
|
||||
$vResource->SetComponents(array());
|
||||
}
|
||||
|
||||
return $ics;
|
||||
return $vResource;
|
||||
}
|
||||
|
||||
@ -2,103 +2,22 @@
|
||||
/**
|
||||
* Handle the FREE-BUSY-QUERY variant of REPORT
|
||||
*/
|
||||
include_once("iCalendar.php");
|
||||
include_once("RRule.php");
|
||||
include_once("freebusy-functions.php");
|
||||
|
||||
$fbq_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:free-busy-query');
|
||||
$fbq_start = $fbq_content[0]->GetAttribute('start');
|
||||
$fbq_end = $fbq_content[0]->GetAttribute('end');
|
||||
|
||||
if ( ! ( isset($fbq_start) || isset($fbq_end) ) ) {
|
||||
$request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
|
||||
}
|
||||
$params = array( ':path_match' => '^'.$request->path.$request->DepthRegexTail(), ':start' => $fbq_start, ':end' => $fbq_end );
|
||||
$where = ' WHERE caldav_data.dav_name ~ :path_match ';
|
||||
$where .= 'AND rrule_event_overlaps( dtstart, dtend, rrule, :start, :end) ';
|
||||
$where .= "AND caldav_data.caldav_type IN ( 'VEVENT', 'VFREEBUSY' ) ";
|
||||
$where .= "AND (calendar_item.transp != 'TRANSPARENT' OR calendar_item.transp IS NULL) ";
|
||||
$where .= "AND (calendar_item.status != 'CANCELLED' OR calendar_item.status IS NULL) ";
|
||||
$range_start = new RepeatRuleDateTime($fbq_start);
|
||||
$range_end = new RepeatRuleDateTime($fbq_end);
|
||||
|
||||
if ( $request->Privileges() != privilege_to_bits('all') ) {
|
||||
$where .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
|
||||
}
|
||||
|
||||
$busy = array();
|
||||
$busy_tentative = array();
|
||||
$sql = "SELECT caldav_data.caldav_data, calendar_item.rrule, calendar_item.transp, calendar_item.status, ";
|
||||
$sql .= "to_char(calendar_item.dtstart at time zone 'GMT',".iCalendar::SqlDateFormat().") AS start, ";
|
||||
$sql .= "to_char(calendar_item.dtend at time zone 'GMT',".iCalendar::SqlDateFormat().") AS finish ";
|
||||
$sql .= "FROM caldav_data INNER JOIN calendar_item USING(dav_id,user_no,dav_name)".$where;
|
||||
if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY dav_id";
|
||||
$qry = new AwlQuery( $sql, $params );
|
||||
if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ) {
|
||||
while( $calendar_object = $qry->Fetch() ) {
|
||||
if ( $calendar_object->transp != "TRANSPARENT" ) {
|
||||
switch ( $calendar_object->status ) {
|
||||
case "TENTATIVE":
|
||||
dbg_error_log( "REPORT", " FreeBusy: tentative appointment: %s, %s", $calendar_object->start, $calendar_object->finish );
|
||||
$busy_tentative[] = $calendar_object;
|
||||
break;
|
||||
/** We use the same code for the REPORT, the POST and the freebusy GET... */
|
||||
$freebusy = get_freebusy( $request->path.$request->DepthRegexTail(), $range_start, $range_end );
|
||||
|
||||
case "CANCELLED":
|
||||
// Cancelled events are ignored
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg_error_log( "REPORT", " FreeBusy: Not transparent, tentative or cancelled: %s, %s", $calendar_object->start, $calendar_object->finish );
|
||||
$busy[] = $calendar_object;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$freebusy = new iCalComponent();
|
||||
$freebusy->SetType('VFREEBUSY');
|
||||
$freebusy->AddProperty('DTSTAMP', date('Ymd\THis\Z'));
|
||||
$freebusy->AddProperty('DTSTART', $fbq_start);
|
||||
$freebusy->AddProperty('DTEND', $fbq_end);
|
||||
|
||||
foreach( $busy_tentative AS $k => $v ) {
|
||||
$start = new iCalDate($v->start);
|
||||
$duration = $start->DateDifference($v->finish);
|
||||
if ( $v->rrule != "" ) {
|
||||
$rrule = new RRule( $start, $v->rrule );
|
||||
while ( $date = $rrule->GetNext() ) {
|
||||
if ( ! $date->GreaterThan($fbq_start) ) continue;
|
||||
if ( $date->GreaterThan($fbq_end) ) break;
|
||||
$todate = clone($date);
|
||||
$todate->AddDuration($duration);
|
||||
$freebusy->AddProperty( 'FREEBUSY', $date->RenderGMT().'/'.$todate->RenderGMT(), array('FBTYPE' => 'BUSY-TENTATIVE') );
|
||||
}
|
||||
}
|
||||
else {
|
||||
$freebusy->AddProperty( 'FREEBUSY', $start->RenderGMT().'/'.$v->finish, array('FBTYPE' => 'BUSY-TENTATIVE') );
|
||||
}
|
||||
}
|
||||
|
||||
foreach( $busy AS $k => $v ) {
|
||||
$start = new iCalDate($v->start);
|
||||
$duration = $start->DateDifference($v->finish);
|
||||
if ( $v->rrule != "" ) {
|
||||
$rrule = new RRule( $start, $v->rrule );
|
||||
while ( $date = $rrule->GetNext() ) {
|
||||
if ( ! $date->GreaterThan($fbq_start) ) continue;
|
||||
if ( $date->GreaterThan($fbq_end) ) break;
|
||||
$todate = clone($date);
|
||||
$todate->AddDuration($duration);
|
||||
$freebusy->AddProperty( 'FREEBUSY', $date->RenderGMT().'/'.$todate->RenderGMT() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
$freebusy->AddProperty( 'FREEBUSY', $start->RenderGMT().'/'.$v->finish );
|
||||
}
|
||||
}
|
||||
|
||||
$result = new iCalComponent();
|
||||
$result->VCalendar();
|
||||
$result->AddComponent($freebusy);
|
||||
|
||||
$request->DoResponse( 200, $result->Render(), 'text/calendar' );
|
||||
$request->DoResponse( 200, $freebusy, 'text/calendar' );
|
||||
// Won't return from that
|
||||
|
||||
|
||||
78
inc/freebusy-functions.php
Normal file
78
inc/freebusy-functions.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Function to include which handles building a free/busy response to
|
||||
* be used in either the REPORT, response to a POST, or response to a
|
||||
* a freebusy GET request.
|
||||
*/
|
||||
|
||||
include_once("RRule-v2.php");
|
||||
|
||||
|
||||
function get_freebusy( $path_match, $range_start, $range_end ) {
|
||||
global $request;
|
||||
|
||||
if ( !isset($range_start) || !isset($range_end) ) {
|
||||
$request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
|
||||
}
|
||||
$params = array( ':path_match' => '^'.$path_match, ':start' => $range_start->UTC(), ':end' => $range_end->UTC() );
|
||||
$where = ' WHERE caldav_data.dav_name ~ :path_match ';
|
||||
$where .= 'AND rrule_event_overlaps( dtstart, dtend, rrule, :start, :end) ';
|
||||
$where .= "AND caldav_data.caldav_type IN ( 'VEVENT', 'VTODO' ) ";
|
||||
$where .= "AND (calendar_item.transp != 'TRANSPARENT' OR calendar_item.transp IS NULL) ";
|
||||
$where .= "AND (calendar_item.status != 'CANCELLED' OR calendar_item.status IS NULL) ";
|
||||
|
||||
if ( $request->Privileges() != privilege_to_bits('all') ) {
|
||||
$where .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
|
||||
}
|
||||
|
||||
|
||||
$fbtimes = array();
|
||||
$sql = "SELECT caldav_data.caldav_data, calendar_item.rrule, calendar_item.transp, calendar_item.status, ";
|
||||
$sql .= "to_char(calendar_item.dtstart at time zone 'GMT',".iCalendar::SqlUTCFormat().") AS start, ";
|
||||
$sql .= "to_char(calendar_item.dtend at time zone 'GMT',".iCalendar::SqlUTCFormat().") AS finish ";
|
||||
$sql .= "FROM caldav_data INNER JOIN calendar_item USING(dav_id,user_no,dav_name)".$where;
|
||||
if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY dav_id";
|
||||
$qry = new AwlQuery( $sql, $params );
|
||||
if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ) {
|
||||
while( $calendar_object = $qry->Fetch() ) {
|
||||
$extra = '';
|
||||
if ( $calendar_object->status == 'TENTATIVE' ) {
|
||||
$extra = ';BUSY-TENTATIVE';
|
||||
}
|
||||
dbg_error_log( "REPORT", " FreeBusy: Not transparent, tentative or cancelled: %s, %s", $calendar_object->start, $calendar_object->finish );
|
||||
$ics = new vComponent($calendar_object->caldav_data);
|
||||
$expanded = expand_event_instances($ics, $range_start, $range_end);
|
||||
$expansion = $expanded->GetComponents( array('VEVENT','VTODO','VJOURNAL') );
|
||||
foreach( $expansion AS $k => $v ) {
|
||||
$dtstart = $v->GetProperty('DTSTART');
|
||||
$start_date = new RepeatRuleDateTime($dtstart->Value());
|
||||
$duration = $v->GetProperty('DURATION');
|
||||
$end_date = clone($start_date);
|
||||
$end_date->modify( $duration->Value() );
|
||||
$thisfb = $start_date->UTC() .'/'. $end_date->UTC() . $extra;
|
||||
array_push( $fbtimes, $thisfb );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$freebusy = new iCalComponent();
|
||||
$freebusy->SetType('VFREEBUSY');
|
||||
$freebusy->AddProperty('DTSTAMP', date('Ymd\THis\Z'));
|
||||
$freebusy->AddProperty('DTSTART', $range_start->UTC());
|
||||
$freebusy->AddProperty('DTEND', $range_end->UTC());
|
||||
|
||||
sort( $fbtimes );
|
||||
foreach( $fbtimes AS $k => $v ) {
|
||||
$text = explode(';',$v,2);
|
||||
$freebusy->AddProperty( 'FREEBUSY', $text[0], (isset($text[1]) ? array('FBTYPE' => $text[1]) : null) );
|
||||
}
|
||||
|
||||
|
||||
$result = new iCalComponent();
|
||||
$result->VCalendar();
|
||||
$result->AddComponent($freebusy);
|
||||
|
||||
return $result->Render();
|
||||
}
|
||||
|
||||
33
testing/tests/regression-suite/0886-REPORT-freebusy.test
Normal file
33
testing/tests/regression-suite/0886-REPORT-freebusy.test
Normal file
@ -0,0 +1,33 @@
|
||||
#
|
||||
# Request a freebusy report by URL
|
||||
#
|
||||
TYPE=REPORT
|
||||
URL=http://mycaldav/caldav.php/user1/home/
|
||||
HEADER=User-Agent: DAViCalTester/public
|
||||
HEADER=Content-Type: text/xml; charset="UTF-8"
|
||||
HEAD
|
||||
|
||||
REPLACE=/^DTSTAMP:\d{8}T\d{6}Z\r?$/DTSTAMP:yyyymmddThhmmssZ/
|
||||
REPLACE=/^DTSTART:20060930T120000Z\r?$/DTSTART:correct/
|
||||
REPLACE=/^DTEND:20070630T115959Z\r?$/DTEND:correct/
|
||||
|
||||
BEGINDATA
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<free-busy-query xmlns:D="DAV:" xmlns="urn:ietf:params:xml:ns:caldav">
|
||||
<time-range start="20060930T120000Z" end="20070630T115959Z"/>
|
||||
</free-busy-query>
|
||||
ENDDATA
|
||||
|
||||
|
||||
QUERY
|
||||
SELECT dav_name "Dav Name", calendar_item.rrule, status,
|
||||
to_char(calendar_item.dtstart at time zone 'GMT','YYYYMMDD"T"HH24MISS"Z"') AS "a) start",
|
||||
to_char(calendar_item.dtend at time zone 'GMT','YYYYMMDD"T"HH24MISS"Z"') AS "b)finish"
|
||||
FROM caldav_data INNER JOIN calendar_item USING(dav_id,user_no,dav_name)
|
||||
WHERE caldav_data.user_no = 10
|
||||
AND rrule_event_overlaps( dtstart, dtend, rrule, '20061001T000000', '20070630T235959')
|
||||
AND caldav_data.caldav_type IN ( 'VEVENT', 'VFREEBUSY' )
|
||||
AND (calendar_item.status != 'CANCELLED' OR calendar_item.status IS NULL)
|
||||
AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL)
|
||||
ORDER BY 2, 3
|
||||
ENDQUERY
|
||||
Loading…
x
Reference in New Issue
Block a user