mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-05-26 02:44:29 +00:00
Merge branch 'master' of git+ssh://repo.or.cz/srv/git/davical
This commit is contained in:
commit
84845b1359
@ -42,14 +42,26 @@ $c->pg_connect[] = "dbname=davical user=davical_app";
|
|||||||
$c->hide_alarm = true;
|
$c->hide_alarm = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*default is false
|
* default is false
|
||||||
*If true, then TODO requested from someone other than the admmin or owner
|
* If true, then TODO requested from someone other than the admmin or owner
|
||||||
* of a calendar will not get any answer. Often these todo are only relevant
|
* of a calendar will not get any answer. Often these todo are only relevant
|
||||||
* to the owner, but in some shared calendar situations they might not be in
|
* to the owner, but in some shared calendar situations they might not be in
|
||||||
* which case you should let this default to false.
|
* which case you should let this default to false.
|
||||||
*/
|
*/
|
||||||
$c->hide_TODO = true;
|
$c->hide_TODO = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default is false for backward compatibility
|
||||||
|
* If true, then calendars accessed via WebDAV will only be readonly. Any
|
||||||
|
* changes to them must be applied via CalDAV.
|
||||||
|
*
|
||||||
|
* You may want to set this to false during your initial setup to make it
|
||||||
|
* easier for people to PUT whole calendars as part of the conversion of
|
||||||
|
* their data. After this it is recommended to turn it off so that clients
|
||||||
|
* which have been misconfigured are readily identifiable.
|
||||||
|
*/
|
||||||
|
$c->readonly_webdav_collections = true;
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* *
|
* *
|
||||||
* ADMIN web Interface *
|
* ADMIN web Interface *
|
||||||
|
|||||||
@ -378,3 +378,55 @@ BEGIN
|
|||||||
RETURN newname;
|
RETURN newname;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
|
||||||
|
DROP TRIGGER caldav_data_modified ON caldav_data CASCADE;
|
||||||
|
CREATE or REPLACE FUNCTION caldav_data_modified() RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
coll_id caldav_data.collection_id%TYPE;
|
||||||
|
BEGIN
|
||||||
|
IF TG_OP = 'UPDATE' THEN
|
||||||
|
IF NEW.caldav_data = OLD.caldav_data AND NEW.collection_id = OLD.collection_id THEN
|
||||||
|
-- Nothing for us to do
|
||||||
|
RETURN NEW;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF TG_OP = 'DELETE' OR TG_OP = 'UPDATE' THEN
|
||||||
|
UPDATE collection
|
||||||
|
SET modified = current_timestamp, dav_etag = md5(OLD.user_no::text||OLD.dav_name||current_timestamp::text)
|
||||||
|
WHERE collection_id = OLD.collection_id;
|
||||||
|
IF TG_OP = 'DELETE' THEN
|
||||||
|
RETURN OLD;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF TG_OP = 'INSERT' THEN
|
||||||
|
UPDATE collection
|
||||||
|
SET modified = current_timestamp, dav_etag = md5(NEW.user_no::text||NEW.dav_name||current_timestamp::text)
|
||||||
|
WHERE collection_id = NEW.collection_id;
|
||||||
|
RETURN NEW;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NEW.collection_id != OLD.collection_id THEN
|
||||||
|
UPDATE collection
|
||||||
|
SET modified = current_timestamp, dav_etag = md5(NEW.user_no::text||NEW.dav_name||current_timestamp::text)
|
||||||
|
WHERE collection_id = NEW.collection_id;
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
CREATE TRIGGER caldav_data_modified AFTER INSERT OR UPDATE OR DELETE ON caldav_data
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE caldav_data_modified();
|
||||||
|
|
||||||
|
/*
|
||||||
|
DROP TRIGGER collection_modified ON collection CASCADE;
|
||||||
|
CREATE or REPLACE FUNCTION collection_modified() RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
CREATE TRIGGER collection_modified AFTER INSERT OR UPDATE ON collection
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE collection_modified();
|
||||||
|
*/
|
||||||
@ -71,6 +71,7 @@ CREATE TABLE calendar_item (
|
|||||||
percent_complete NUMERIC(7,2),
|
percent_complete NUMERIC(7,2),
|
||||||
tz_id TEXT REFERENCES time_zone( tz_id ),
|
tz_id TEXT REFERENCES time_zone( tz_id ),
|
||||||
status TEXT,
|
status TEXT,
|
||||||
|
completed TIMESTAMP WITH TIME ZONE,
|
||||||
dav_id INT8 UNIQUE,
|
dav_id INT8 UNIQUE,
|
||||||
collection_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
|
collection_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
|
||||||
|
|
||||||
|
|||||||
16
dba/patches/1.2.4.sql
Normal file
16
dba/patches/1.2.4.sql
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
-- This database update provides new tables for the Principal, for
|
||||||
|
-- a consistent dav_resource which a principal, collection or calendar_item
|
||||||
|
-- all inherit from.
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
SELECT check_db_revision(1,2,3);
|
||||||
|
|
||||||
|
-- Add a column to hold the 'COMPLETED' property from the caldav_data
|
||||||
|
ALTER TABLE calendar_item ADD COLUMN completed TIMESTAMP WITH TIME ZONE;
|
||||||
|
|
||||||
|
SELECT new_db_revision(1,2,4, 'Avril' );
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
@ -3,24 +3,24 @@
|
|||||||
*
|
*
|
||||||
* @package rscds
|
* @package rscds
|
||||||
* @subpackage database
|
* @subpackage database
|
||||||
* @author Andrew McMillan <andrew@catalyst.net.nz>
|
* @author Andrew McMillan <andrew@mcmillan.net.nz>
|
||||||
* @copyright Catalyst IT Ltd
|
* @copyright Morphoss Ltd - http://www.morphoss.com/
|
||||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
|
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
-- How many days are there in a particular month?
|
-- How many days are there in a particular month?
|
||||||
CREATE or REPLACE FUNCTION rrule_days_in_month( TIMESTAMP WITH TIME ZONE ) RETURNS INT AS '
|
CREATE or REPLACE FUNCTION rrule_days_in_month( TIMESTAMP WITH TIME ZONE ) RETURNS INT AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
in_time ALIAS FOR $1;
|
in_time ALIAS FOR $1;
|
||||||
days INT;
|
days INT;
|
||||||
BEGIN
|
BEGIN
|
||||||
RETURN date_part( ''days'', date_trunc( ''month'', in_time + interval ''1 month'') - interval ''1 day'' );
|
RETURN date_part( 'days', date_trunc( 'month', in_time + interval '1 month') - interval '1 day' );
|
||||||
END;
|
END;
|
||||||
' LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
||||||
|
|
||||||
|
|
||||||
-- Return a SETOF text strings, split on the commas in the original one
|
-- Return a SETOF text strings, split on the commas in the original one
|
||||||
CREATE or REPLACE FUNCTION rrule_split_on_commas( TEXT ) RETURNS SETOF TEXT AS '
|
CREATE or REPLACE FUNCTION rrule_split_on_commas( TEXT ) RETURNS SETOF TEXT AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
in_text ALIAS FOR $1;
|
in_text ALIAS FOR $1;
|
||||||
part TEXT;
|
part TEXT;
|
||||||
@ -29,7 +29,7 @@ DECLARE
|
|||||||
BEGIN
|
BEGIN
|
||||||
remainder := in_text;
|
remainder := in_text;
|
||||||
LOOP
|
LOOP
|
||||||
cpos := position( '','' in remainder );
|
cpos := position( ',' in remainder );
|
||||||
IF cpos = 0 THEN
|
IF cpos = 0 THEN
|
||||||
part := remainder;
|
part := remainder;
|
||||||
EXIT;
|
EXIT;
|
||||||
@ -42,10 +42,10 @@ BEGIN
|
|||||||
RETURN NEXT part;
|
RETURN NEXT part;
|
||||||
RETURN;
|
RETURN;
|
||||||
END;
|
END;
|
||||||
' LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
||||||
|
|
||||||
-- Return a SETOF dates within the month of a particular date which match a single BYDAY rule specification
|
-- Return a SETOF dates within the month of a particular date which match a single BYDAY rule specification
|
||||||
CREATE or REPLACE FUNCTION rrule_month_bydayrule_set( TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS '
|
CREATE or REPLACE FUNCTION rrule_month_bydayrule_set( TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
in_time ALIAS FOR $1;
|
in_time ALIAS FOR $1;
|
||||||
bydayrule ALIAS FOR $2;
|
bydayrule ALIAS FOR $2;
|
||||||
@ -55,54 +55,54 @@ DECLARE
|
|||||||
each_day TIMESTAMP WITH TIME ZONE;
|
each_day TIMESTAMP WITH TIME ZONE;
|
||||||
this_month INT;
|
this_month INT;
|
||||||
BEGIN
|
BEGIN
|
||||||
dow := position(substring( bydayrule from ''..$'') in ''SUMOTUWETHFRSA'') / 2;
|
dow := position(substring( bydayrule from '..$') in 'SUMOTUWETHFRSA') / 2;
|
||||||
each_day := date_trunc( ''month'', in_time ) + (in_time::time)::interval;
|
each_day := date_trunc( 'month', in_time ) + (in_time::time)::interval;
|
||||||
this_month := date_part( ''month'', in_time );
|
this_month := date_part( 'month', in_time );
|
||||||
first_dow := date_part( ''dow'', each_day );
|
first_dow := date_part( 'dow', each_day );
|
||||||
each_day := each_day - ( first_dow::text || ''days'')::interval
|
each_day := each_day - ( first_dow::text || 'days')::interval
|
||||||
+ ( dow::text || ''days'')::interval
|
+ ( dow::text || 'days')::interval
|
||||||
+ CASE WHEN dow > first_dow THEN ''1 week''::interval ELSE ''0s''::interval END;
|
+ CASE WHEN dow > first_dow THEN '1 week'::interval ELSE '0s'::interval END;
|
||||||
|
|
||||||
IF length(bydayrule) > 2 THEN
|
IF length(bydayrule) > 2 THEN
|
||||||
index := (substring(bydayrule from ''^[0-9-]+''))::int;
|
index := (substring(bydayrule from '^[0-9-]+'))::int;
|
||||||
|
|
||||||
-- Possibly we should check that (index != 0) here, which is an error
|
-- Possibly we should check that (index != 0) here, which is an error
|
||||||
|
|
||||||
IF index = 0 THEN
|
IF index = 0 THEN
|
||||||
RAISE NOTICE ''Ignored invalid BYDAY rule part "%".'', bydayrule;
|
RAISE NOTICE 'Ignored invalid BYDAY rule part "%".', bydayrule;
|
||||||
ELSIF index > 0 THEN
|
ELSIF index > 0 THEN
|
||||||
-- The simplest case, such as 2MO for the second monday
|
-- The simplest case, such as 2MO for the second monday
|
||||||
each_day := each_day + ((index - 1)::text || '' weeks'')::interval;
|
each_day := each_day + ((index - 1)::text || ' weeks')::interval;
|
||||||
ELSE
|
ELSE
|
||||||
each_day := each_day + ''5 weeks''::interval;
|
each_day := each_day + '5 weeks'::interval;
|
||||||
WHILE date_part(''month'', each_day) != this_month LOOP
|
WHILE date_part('month', each_day) != this_month LOOP
|
||||||
each_day := each_day - ''1 week''::interval;
|
each_day := each_day - '1 week'::interval;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
-- Note that since index is negative, (-2 + 1) == -1, for example
|
-- Note that since index is negative, (-2 + 1) == -1, for example
|
||||||
each_day := each_day + ( (index + 1)::text || '' weeks'')::interval ;
|
each_day := each_day + ( (index + 1)::text || ' weeks')::interval ;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Sometimes (e.g. 5TU or -5WE) there might be no such date in some months
|
-- Sometimes (e.g. 5TU or -5WE) there might be no such date in some months
|
||||||
IF date_part(''month'', each_day) = this_month THEN
|
IF date_part('month', each_day) = this_month THEN
|
||||||
RETURN NEXT each_day;
|
RETURN NEXT each_day;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
ELSE
|
ELSE
|
||||||
-- Return all such days that are within the given month
|
-- Return all such days that are within the given month
|
||||||
WHILE date_part(''month'', each_day) = this_month LOOP
|
WHILE date_part('month', each_day) = this_month LOOP
|
||||||
RETURN NEXT each_day;
|
RETURN NEXT each_day;
|
||||||
each_day := each_day + ''1 week''::interval;
|
each_day := each_day + '1 week'::interval;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
RETURN;
|
RETURN;
|
||||||
|
|
||||||
END;
|
END;
|
||||||
' LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
||||||
|
|
||||||
|
|
||||||
-- Return a SETOF dates within the month of a particular date which match a string of BYDAY rule specifications
|
-- Return a SETOF dates within the month of a particular date which match a string of BYDAY rule specifications
|
||||||
CREATE or REPLACE FUNCTION rrule_month_byday_set( TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS '
|
CREATE or REPLACE FUNCTION rrule_month_byday_set( TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
in_time ALIAS FOR $1;
|
in_time ALIAS FOR $1;
|
||||||
byday ALIAS FOR $2;
|
byday ALIAS FOR $2;
|
||||||
@ -119,13 +119,13 @@ BEGIN
|
|||||||
RETURN;
|
RETURN;
|
||||||
|
|
||||||
END;
|
END;
|
||||||
' LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Return a SETOF dates within the month of a particular date which match a string of BYDAY rule specifications
|
-- Return a SETOF dates within the month of a particular date which match a string of BYDAY rule specifications
|
||||||
CREATE or REPLACE FUNCTION rrule_month_bymonthday_set( TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS '
|
CREATE or REPLACE FUNCTION rrule_month_bymonthday_set( TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
in_time ALIAS FOR $1;
|
in_time ALIAS FOR $1;
|
||||||
bymonthday ALIAS FOR $2;
|
bymonthday ALIAS FOR $2;
|
||||||
@ -136,21 +136,21 @@ DECLARE
|
|||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
daysinmonth := rrule_days_in_month(in_time);
|
daysinmonth := rrule_days_in_month(in_time);
|
||||||
month_start := date_trunc( ''month'', in_time ) + (in_time::time)::interval;
|
month_start := date_trunc( 'month', in_time ) + (in_time::time)::interval;
|
||||||
|
|
||||||
FOR dayrule IN SELECT * FROM rrule_split_on_commas( bymonthday ) LOOP
|
FOR dayrule IN SELECT * FROM rrule_split_on_commas( bymonthday ) LOOP
|
||||||
dayoffset := dayrule.rrule_split_on_commas::int;
|
dayoffset := dayrule.rrule_split_on_commas::int;
|
||||||
IF dayoffset = 0 THEN
|
IF dayoffset = 0 THEN
|
||||||
RAISE NOTICE ''Ignored invalid BYMONTHDAY part "%".'', dayrule.rrule_split_on_commas;
|
RAISE NOTICE 'Ignored invalid BYMONTHDAY part "%".', dayrule.rrule_split_on_commas;
|
||||||
dayoffset := 0;
|
dayoffset := 0;
|
||||||
ELSIF dayoffset > daysinmonth THEN
|
ELSIF dayoffset > daysinmonth THEN
|
||||||
dayoffset := 0;
|
dayoffset := 0;
|
||||||
ELSIF dayoffset < (-1 * daysinmonth) THEN
|
ELSIF dayoffset < (-1 * daysinmonth) THEN
|
||||||
dayoffset := 0;
|
dayoffset := 0;
|
||||||
ELSIF dayoffset > 0 THEN
|
ELSIF dayoffset > 0 THEN
|
||||||
RETURN NEXT month_start + ((dayoffset - 1)::text || ''days'')::interval;
|
RETURN NEXT month_start + ((dayoffset - 1)::text || 'days')::interval;
|
||||||
ELSE
|
ELSE
|
||||||
RETURN NEXT month_start + ((daysinmonth + dayoffset)::text || ''days'')::interval;
|
RETURN NEXT month_start + ((daysinmonth + dayoffset)::text || 'days')::interval;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END LOOP;
|
END LOOP;
|
||||||
@ -158,12 +158,12 @@ BEGIN
|
|||||||
RETURN;
|
RETURN;
|
||||||
|
|
||||||
END;
|
END;
|
||||||
' LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Return a SETOF dates within the week of a particular date which match a single BYDAY rule specification
|
-- Return a SETOF dates within the week of a particular date which match a single BYDAY rule specification
|
||||||
CREATE or REPLACE FUNCTION rrule_week_byday_set( TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS '
|
CREATE or REPLACE FUNCTION rrule_week_byday_set( TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
in_time ALIAS FOR $1;
|
in_time ALIAS FOR $1;
|
||||||
byweekday ALIAS FOR $2;
|
byweekday ALIAS FOR $2;
|
||||||
@ -171,21 +171,21 @@ DECLARE
|
|||||||
dow INT;
|
dow INT;
|
||||||
our_day TIMESTAMP WITH TIME ZONE;
|
our_day TIMESTAMP WITH TIME ZONE;
|
||||||
BEGIN
|
BEGIN
|
||||||
our_day := date_trunc( ''week'', in_time ) + (in_time::time)::interval;
|
our_day := date_trunc( 'week', in_time ) + (in_time::time)::interval;
|
||||||
|
|
||||||
FOR dayrule IN SELECT * FROM rrule_split_on_commas( byweekday ) LOOP
|
FOR dayrule IN SELECT * FROM rrule_split_on_commas( byweekday ) LOOP
|
||||||
dow := position(substring( dayrule.rrule_split_on_commas from ''..$'') in ''SUMOTUWETHFRSA'') / 2;
|
dow := position(substring( dayrule.rrule_split_on_commas from '..$') in 'SUMOTUWETHFRSA') / 2;
|
||||||
RETURN NEXT our_day + ((dow - 1)::text || ''days'')::interval;
|
RETURN NEXT our_day + ((dow - 1)::text || 'days')::interval;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
|
|
||||||
RETURN;
|
RETURN;
|
||||||
|
|
||||||
END;
|
END;
|
||||||
' LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
||||||
|
|
||||||
|
|
||||||
CREATE or REPLACE FUNCTION event_has_exceptions( TEXT ) RETURNS BOOLEAN AS '
|
CREATE or REPLACE FUNCTION event_has_exceptions( TEXT ) RETURNS BOOLEAN AS $$
|
||||||
SELECT $1 ~ ''\nRECURRENCE-ID(;TZID=[^:]+)?:[[:space:]]*[[:digit:]]{8}(T[[:digit:]]{6})?''
|
SELECT $1 ~ E'\nRECURRENCE-ID(;TZID=[^:]+)?:[[:space:]]*[[:digit:]]{8}(T[[:digit:]]{6})?'
|
||||||
' LANGUAGE 'sql' IMMUTABLE STRICT;
|
$$ LANGUAGE 'sql' IMMUTABLE STRICT;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,7 @@ switch ( $request->method ) {
|
|||||||
case 'MKCALENDAR': include_once("caldav-MKCALENDAR.php"); break;
|
case 'MKCALENDAR': include_once("caldav-MKCALENDAR.php"); break;
|
||||||
case 'MKCOL': include_once("caldav-MKCALENDAR.php"); break;
|
case 'MKCOL': include_once("caldav-MKCALENDAR.php"); break;
|
||||||
case 'PUT': include_once("caldav-PUT.php"); break;
|
case 'PUT': include_once("caldav-PUT.php"); break;
|
||||||
|
case 'POST': include_once("caldav-POST.php"); break;
|
||||||
case 'GET': include_once("caldav-GET.php"); break;
|
case 'GET': include_once("caldav-GET.php"); break;
|
||||||
case 'HEAD': include_once("caldav-GET.php"); break;
|
case 'HEAD': include_once("caldav-GET.php"); break;
|
||||||
case 'DELETE': include_once("caldav-DELETE.php"); break;
|
case 'DELETE': include_once("caldav-DELETE.php"); break;
|
||||||
|
|||||||
@ -93,6 +93,13 @@ class CalDAVPrincipal
|
|||||||
else if ( isset($parameters['user_no']) ) {
|
else if ( isset($parameters['user_no']) ) {
|
||||||
$usr = getUserByID($parameters['user_no']);
|
$usr = getUserByID($parameters['user_no']);
|
||||||
}
|
}
|
||||||
|
else if ( isset($parameters['email']) && isset($parameters['options']['allow_by_email']) ) {
|
||||||
|
$parameters['options']['allow_by_email'] = false;
|
||||||
|
if ( $username = $this->UsernameFromEMail($parameters['email']) ) {
|
||||||
|
$usr = getUserByName($username);
|
||||||
|
$this->by_email = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if ( isset($parameters['path']) ) {
|
else if ( isset($parameters['path']) ) {
|
||||||
dbg_error_log( "principal", "Finding Principal from path: '%s', options.allow_by_email: '%s'", $parameters['path'], $parameters['options']['allow_by_email'] );
|
dbg_error_log( "principal", "Finding Principal from path: '%s', options.allow_by_email: '%s'", $parameters['path'], $parameters['options']['allow_by_email'] );
|
||||||
if ( $username = $this->UsernameFromPath($parameters['path'], $parameters['options']) ) {
|
if ( $username = $this->UsernameFromPath($parameters['path'], $parameters['options']) ) {
|
||||||
@ -209,6 +216,21 @@ class CalDAVPrincipal
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Work out the username, based on the given e-mail
|
||||||
|
* @param string $email The email address to be used.
|
||||||
|
*/
|
||||||
|
function UsernameFromEMail( $email ) {
|
||||||
|
$qry = new PgQuery("SELECT user_no, username FROM usr WHERE email = ?;", $email );
|
||||||
|
if ( $qry->Exec("principal") && $user = $qry->Fetch() ) {
|
||||||
|
$user_no = $user->user_no;
|
||||||
|
$username = $user->username;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $username;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the array of privilege names converted into XMLElements
|
* Returns the array of privilege names converted into XMLElements
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -64,6 +64,12 @@ class CalDAVRequest
|
|||||||
*/
|
*/
|
||||||
var $collection_path;
|
var $collection_path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of collection being requested:
|
||||||
|
* calendar, schedule-inbox, schedule-outbox
|
||||||
|
*/
|
||||||
|
var $collection_type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new CalDAVRequest object.
|
* Create a new CalDAVRequest object.
|
||||||
*/
|
*/
|
||||||
@ -196,22 +202,44 @@ class CalDAVRequest
|
|||||||
/**
|
/**
|
||||||
* Get the ID of the collection we are referring to
|
* Get the ID of the collection we are referring to
|
||||||
*/
|
*/
|
||||||
if ( !isset($this->collection_id) && preg_match( '#^(/.+/.+/)[^/]*$#', $this->path, $matches ) ) {
|
if ( preg_match( '#^(/.+/.+/)[^/]*$#', $this->path, $matches ) ) {
|
||||||
$qry = new PgQuery( "SELECT collection_id FROM collection WHERE user_no = ? AND dav_name = ?;", $this->user_no, $matches[1] );
|
$this->collection_type = 'calendar';
|
||||||
if ( $qry->Exec('caldav') && $qry->rows == 1 && ($row = $qry->Fetch()) ) {
|
if ( preg_match( '#^(/[^/]+/\.(in|out)/)[^/]*$#', $this->path, $matches ) ) {
|
||||||
$this->collection_id = $row->collection_id;
|
$this->collection_type = $matches[2];
|
||||||
$this->collection_path = $matches[1];
|
}
|
||||||
|
if ( ! isset($this->collection_id) ) {
|
||||||
|
$qry = new PgQuery( "SELECT collection_id FROM collection WHERE user_no = ? AND dav_name = ?;",
|
||||||
|
($this->collection_type == 'calendar' ? $this->user_no : $session->user_no), $matches[1] );
|
||||||
|
if ( $qry->Exec('caldav') && $qry->rows == 1 && ($row = $qry->Fetch()) ) {
|
||||||
|
$this->collection_id = $row->collection_id;
|
||||||
|
$this->collection_path = $matches[1];
|
||||||
|
}
|
||||||
|
else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->path, $matches ) ) {
|
||||||
|
// Request is for a scheduling inbox or outbox (or something inside one) and we should auto-create it
|
||||||
|
$displayname = $session->fullname . ($matches[3] == 'in' ? ' Inbox' : ' Outbox');
|
||||||
|
$sql = <<<EOSQL
|
||||||
|
INSERT INTO collection ( user_no, parent_container, dav_name, dav_displayname, is_calendar, created, modified )
|
||||||
|
VALUES( ?, ?, ?, ?, FALSE, current_timestamp, current_timestamp )
|
||||||
|
EOSQL;
|
||||||
|
$qry = new PgQuery( $sql, $session->user_no, $matches[2] , $matches[1], $displayname );
|
||||||
|
$qry->Exec('caldav');
|
||||||
|
dbg_error_log( "caldav", "Created new collection as '$displayname'." );
|
||||||
|
|
||||||
|
$qry = new PgQuery( "SELECT collection_id FROM collection WHERE user_no = ? AND dav_name = ?;", $session->user_no, $matches[1] );
|
||||||
|
if ( $qry->Exec('caldav') && $qry->rows == 1 && ($row = $qry->Fetch()) ) {
|
||||||
|
$this->collection_id = $row->collection_id;
|
||||||
|
$this->collection_path = $matches[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dbg_error_log( "caldav", " Collection '%s' is %d", $this->collection_path, $this->collection_id );
|
dbg_error_log( "caldav", " Collection '%s' is %d, type %s", $this->collection_path, $this->collection_id, $this->collection_type );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluate our permissions for accessing the target
|
* Evaluate our permissions for accessing the target
|
||||||
*/
|
*/
|
||||||
$this->setPermissions();
|
$this->setPermissions();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the content we are receiving is XML then we parse it here. RFC2518 says we
|
* If the content we are receiving is XML then we parse it here. RFC2518 says we
|
||||||
* should reasonably expect to see either text/xml or application/xml
|
* should reasonably expect to see either text/xml or application/xml
|
||||||
@ -236,9 +264,9 @@ class CalDAVRequest
|
|||||||
$this->etag_if_match = str_replace('"','',$_SERVER["HTTP_IF_MATCH"]);
|
$this->etag_if_match = str_replace('"','',$_SERVER["HTTP_IF_MATCH"]);
|
||||||
if ( $this->etag_if_match == '' ) unset($this->etag_if_match);
|
if ( $this->etag_if_match == '' ) unset($this->etag_if_match);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Work out the user whose calendar we are accessing, based on elements of the path.
|
* Work out the user whose calendar we are accessing, based on elements of the path.
|
||||||
*/
|
*/
|
||||||
@ -532,15 +560,30 @@ class CalDAVRequest
|
|||||||
function AllowedTo( $activity ) {
|
function AllowedTo( $activity ) {
|
||||||
if ( isset($this->permissions['all']) ) return true;
|
if ( isset($this->permissions['all']) ) return true;
|
||||||
switch( $activity ) {
|
switch( $activity ) {
|
||||||
|
case "CALDAV:schedule-send-freebusy":
|
||||||
|
return isset($this->permissions['read']) || isset($this->permissions['freebusy']);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "CALDAV:schedule-send-invite":
|
||||||
|
return isset($this->permissions['read']) || isset($this->permissions['freebusy']);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "CALDAV:schedule-send-reply":
|
||||||
|
return isset($this->permissions['read']) || isset($this->permissions['freebusy']);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'freebusy':
|
case 'freebusy':
|
||||||
return isset($this->permissions['read']) || isset($this->permissions['freebusy']);
|
return isset($this->permissions['read']) || isset($this->permissions['freebusy']);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'delete':
|
case 'delete':
|
||||||
return isset($this->permissions['write']) || isset($this->permissions['unbind']);
|
return isset($this->permissions['write']) || isset($this->permissions['unbind']);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'proppatch':
|
case 'proppatch':
|
||||||
return isset($this->permissions['write']) || isset($this->permissions['write-properties']);
|
return isset($this->permissions['write']) || isset($this->permissions['write-properties']);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'modify':
|
case 'modify':
|
||||||
return isset($this->permissions['write']) || isset($this->permissions['write-content']);
|
return isset($this->permissions['write']) || isset($this->permissions['write-content']);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -252,7 +252,7 @@ class iCalDate {
|
|||||||
* Add some integer number of days to a date
|
* Add some integer number of days to a date
|
||||||
*/
|
*/
|
||||||
function AddDays( $dd ) {
|
function AddDays( $dd ) {
|
||||||
dbg_error_log( "RRule", " Adding %d days to %s", $dd, $this->_text );
|
$at_start = $this->_text;
|
||||||
$this->_dd += $dd;
|
$this->_dd += $dd;
|
||||||
while ( 1 > $this->_dd ) {
|
while ( 1 > $this->_dd ) {
|
||||||
$this->_mo--;
|
$this->_mo--;
|
||||||
@ -272,7 +272,7 @@ class iCalDate {
|
|||||||
}
|
}
|
||||||
$this->_EpochFromParts();
|
$this->_EpochFromParts();
|
||||||
$this->_TextFromEpoch();
|
$this->_TextFromEpoch();
|
||||||
dbg_error_log( "RRule", " Added %d days and got %s", $dd, $this->_text );
|
dbg_error_log( "RRule", " Added %d days to %s and got %s", $dd, $at_start, $this->_text );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -436,20 +436,23 @@ class iCalDate {
|
|||||||
/**
|
/**
|
||||||
* Applies any BYDAY to the week to return a set of days
|
* Applies any BYDAY to the week to return a set of days
|
||||||
* @param string $byday The BYDAY rule
|
* @param string $byday The BYDAY rule
|
||||||
|
* @param string $increasing When we are moving by months, we want any day of the week, but when by day we only want to increase. Default false.
|
||||||
* @return array An array of the day numbers for the week which meet the rule.
|
* @return array An array of the day numbers for the week which meet the rule.
|
||||||
*/
|
*/
|
||||||
function GetWeekByDay($byday) {
|
function GetWeekByDay($byday, $increasing = false) {
|
||||||
global $ical_weekdays;
|
global $ical_weekdays;
|
||||||
dbg_error_log( "RRule", " Applying BYDAY %s to week", $byday );
|
dbg_error_log( "RRule", " Applying BYDAY %s to week", $byday );
|
||||||
$days = split(',',$byday);
|
$days = split(',',$byday);
|
||||||
$dow = date('w',$this->_epoch);
|
$dow = date('w',$this->_epoch);
|
||||||
|
$set = array();
|
||||||
foreach( $days AS $k => $v ) {
|
foreach( $days AS $k => $v ) {
|
||||||
$daynum = $ical_weekdays[$v];
|
$daynum = $ical_weekdays[$v];
|
||||||
$dd = $this->_dd - $dow + $daynum;
|
$dd = $this->_dd - $dow + $daynum;
|
||||||
if ( $daynum < $this->_wkst ) $dd += 7;
|
if ( $daynum < $this->_wkst ) $dd += 7;
|
||||||
$set[$dd] = $dd;
|
if ( $dd > $this->_dd || !$increasing ) $set[$dd] = $dd;
|
||||||
}
|
}
|
||||||
asort( $set, SORT_NUMERIC );
|
asort( $set, SORT_NUMERIC );
|
||||||
|
|
||||||
return $set;
|
return $set;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -893,15 +896,15 @@ class RRule {
|
|||||||
$limit = 100;
|
$limit = 100;
|
||||||
do {
|
do {
|
||||||
$limit--;
|
$limit--;
|
||||||
if ( $this->_started ) {
|
if ( $this->_started ) {
|
||||||
$next->AddDays($this->_part['INTERVAL']);
|
$next->AddDays($this->_part['INTERVAL']);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$this->_started = true;
|
$this->_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset($this->_part['BYDAY']) ) {
|
if ( isset($this->_part['BYDAY']) ) {
|
||||||
$days = $next->GetWeekByDay($this->_part['BYDAY']);
|
$days = $next->GetWeekByDay($this->_part['BYDAY'], true );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
$days[$next->_dd] = $next->_dd;
|
$days[$next->_dd] = $next->_dd;
|
||||||
|
|||||||
@ -10,12 +10,15 @@
|
|||||||
*/
|
*/
|
||||||
dbg_error_log("POST", "method handler");
|
dbg_error_log("POST", "method handler");
|
||||||
|
|
||||||
|
require_once("XMLDocument.php");
|
||||||
require_once("iCalendar.php");
|
require_once("iCalendar.php");
|
||||||
|
include_once("RRule.php");
|
||||||
|
|
||||||
if ( ! $request->AllowedTo("CALDAV:schedule-send-freebusy")
|
if ( ! $request->AllowedTo("CALDAV:schedule-send-freebusy")
|
||||||
&& ! $request->AllowedTo("CALDAV:schedule-send-invite")
|
&& ! $request->AllowedTo("CALDAV:schedule-send-invite")
|
||||||
&& ! $request->AllowedTo("CALDAV:schedule-send-reply") ) {
|
&& ! $request->AllowedTo("CALDAV:schedule-send-reply") ) {
|
||||||
$request->DoResponse(403);
|
// $request->DoResponse(403);
|
||||||
|
dbg_error_log( "WARN", ": POST: permissions not yet checked" );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || $c->dbg['post']) ) {
|
if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || $c->dbg['post']) ) {
|
||||||
@ -27,11 +30,140 @@ if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || $c->dbg['post']) ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handle_freebusy_request( $ic ) {
|
||||||
|
global $c, $session, $request;
|
||||||
|
|
||||||
|
$reply = new XMLDocument( array("DAV:" => "", "urn:ietf:params:xml:ns:caldav" => "C" ) );
|
||||||
|
$responses = array();
|
||||||
|
|
||||||
|
$fbq_start = $ic->Get('DTSTART');
|
||||||
|
$fbq_end = $ic->Get('DTEND');
|
||||||
|
$component =& $ic->component->FirstNonTimezone();
|
||||||
|
$attendees = $component->GetProperties('ATTENDEE');
|
||||||
|
|
||||||
|
$fb_response_template = new iCalendar( array( 'DTSTAMP' => date('Ymd\THis\Z'),
|
||||||
|
'DTSTART' => $fbq_start,
|
||||||
|
'DTEND' => $fbq_end,
|
||||||
|
'UID' => $ic->Get('UID'),
|
||||||
|
'ORGANIZER' => $ic->Get('ORGANIZER'),
|
||||||
|
'type' => 'VFREEBUSY' ) );
|
||||||
|
$fb_response_template->component->AddProperty( new iCalProp("METHOD:REPLY") );
|
||||||
|
|
||||||
|
foreach( $attendees AS $k => $attendee ) {
|
||||||
|
$attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
|
||||||
|
if ( ! ( isset($fbq_start) || isset($fbq_end) ) ) {
|
||||||
|
$request->DoResponse( 400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND' );
|
||||||
|
}
|
||||||
|
$where = " WHERE usr.email = ? AND collection.is_calendar ";
|
||||||
|
if ( isset( $fbq_start ) ) {
|
||||||
|
$where .= "AND (dtend >= ".qpg($fbq_start)."::timestamp with time zone ";
|
||||||
|
$where .= "OR calculate_later_timestamp(".qpg($fbq_start)."::timestamp with time zone,dtend,rrule) >= ".qpg($fbq_start)."::timestamp with time zone) ";
|
||||||
|
}
|
||||||
|
if ( isset( $fbq_end ) ) {
|
||||||
|
$where .= "AND dtstart <= ".qpg($fbq_end)."::timestamp with time zone ";
|
||||||
|
}
|
||||||
|
$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) ";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @TODO Some significant permissions need to be added around the visibility of free/busy
|
||||||
|
* but lets get it working first...
|
||||||
|
*/
|
||||||
|
$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 usr INNER JOIN collection USING (user_no) INNER JOIN caldav_data USING (collection_id) INNER JOIN calendar_item USING(dav_id)".$where;
|
||||||
|
if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY dav_id";
|
||||||
|
$qry = new PgQuery( $sql, $attendee_email );
|
||||||
|
if ( $qry->Exec("POST",__LINE__,__FILE__) && $qry->rows > 0 ) {
|
||||||
|
while( $calendar_object = $qry->Fetch() ) {
|
||||||
|
if ( $calendar_object->transp != "TRANSPARENT" ) {
|
||||||
|
switch ( $calendar_object->status ) {
|
||||||
|
case "TENTATIVE":
|
||||||
|
dbg_error_log( "POST", " FreeBusy: tentative appointment: %s, %s", $calendar_object->start, $calendar_object->finish );
|
||||||
|
$busy_tentative[] = $calendar_object;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "CANCELLED":
|
||||||
|
// Cancelled events are ignored
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dbg_error_log( "POST", " FreeBusy: Not transparent, tentative or cancelled: %s, %s", $calendar_object->start, $calendar_object->finish );
|
||||||
|
$busy[] = $calendar_object;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
$fbparams = array( "FBTYPE" => "BUSY-TENTATIVE" );
|
||||||
|
$fb = clone($fb_response_template);
|
||||||
|
$fb->Add( $attendee->Name(), $attendee->Value(), $attendee->parameters );
|
||||||
|
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);
|
||||||
|
$fb->Add("FREEBUSY", sprintf("%s/%s", $date->Render('Ymd\THis'), $todate->Render('Ymd\THis') ), $fbparams );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$fb->Add("FREEBUSY;FBTYPE=BUSY-TENTATIVE",sprintf("%s/%s", $start->Render('Ymd\THis'), $v->finish ), $fbparams );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
$fb->Add("FREEBUSY", sprintf("%s/%s", $date->Render('Ymd\THis'), $todate->Render('Ymd\THis') ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$fb->Add("FREEBUSY", sprintf("%s/%s", $start->Render('Ymd\THis'), $v->finish ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = new XMLElement( $reply->Caldav("response") );
|
||||||
|
$response->NewElement( $reply->Caldav("recipient"), new XMLElement("href",$_SERVER['HTTP_RECIPIENT']) );
|
||||||
|
$response->NewElement( $reply->Caldav("request-status"), "2.0;Success" ); // Cargo-cult setting
|
||||||
|
$response->NewElement( $reply->Caldav("calendar-data"), $fb->Render() );
|
||||||
|
$responses[] = $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = new XMLElement( "schedule-response", $responses, $reply->GetXmlNsArray() );
|
||||||
|
$request->DoResponse( 200, $response->Render() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$ical = new iCalendar( array('icalendar' => $request->raw_post) );
|
$ical = new iCalendar( array('icalendar' => $request->raw_post) );
|
||||||
switch ( $ical->properties['METHOD'] ) {
|
$calendar_properties = $ical->component->GetProperties('METHOD');
|
||||||
|
$method = $calendar_properties[0]->Value();
|
||||||
|
switch ( $method ) {
|
||||||
case 'REQUEST':
|
case 'REQUEST':
|
||||||
|
dbg_error_log("POST", ": Handling iTIP 'REQUEST' method.", $method );
|
||||||
|
handle_freebusy_request( $ical );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dbg_error_log("POST", ": Unhandled '%s' method in request.", $ical->properties['METHOD'] );
|
dbg_error_log("POST", ": Unhandled '%s' method in request.", $method );
|
||||||
}
|
}
|
||||||
|
|||||||
@ -427,8 +427,6 @@ function item_to_xml( $item ) {
|
|||||||
|
|
||||||
$allprop = isset($prop_list['DAV::allprop']);
|
$allprop = isset($prop_list['DAV::allprop']);
|
||||||
|
|
||||||
$item->properties = get_arbitrary_properties($item->dav_name);
|
|
||||||
|
|
||||||
$url = ConstructURL($item->dav_name);
|
$url = ConstructURL($item->dav_name);
|
||||||
|
|
||||||
$prop = new XMLElement("prop");
|
$prop = new XMLElement("prop");
|
||||||
@ -464,6 +462,8 @@ function item_to_xml( $item ) {
|
|||||||
*/
|
*/
|
||||||
add_general_properties( $prop, $not_found, $denied, $item );
|
add_general_properties( $prop, $not_found, $denied, $item );
|
||||||
|
|
||||||
|
add_arbitrary_properties($prop, $not_found, $item );
|
||||||
|
|
||||||
return build_propstat_response( $prop, $not_found, $denied, $url );
|
return build_propstat_response( $prop, $not_found, $denied, $url );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -124,6 +124,15 @@ function calendar_to_xml( $properties, $item ) {
|
|||||||
break;
|
break;
|
||||||
case 'resourcetype':
|
case 'resourcetype':
|
||||||
$prop->NewElement($k, new XMLElement($reply->Caldav("calendar"), false) );
|
$prop->NewElement($k, new XMLElement($reply->Caldav("calendar"), false) );
|
||||||
|
if ( $request->collection_type == 'in' ) {
|
||||||
|
$prop->NewElement($k, new XMLElement($reply->Caldav("schedule-inbox"), false) );
|
||||||
|
}
|
||||||
|
else if ( $request->collection_type == 'out' ) {
|
||||||
|
$prop->NewElement($k, new XMLElement($reply->Caldav("schedule-outbox"), false) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$prop->NewElement($k, new XMLElement($reply->Caldav("schedule-calendar"), false) );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'displayname':
|
case 'displayname':
|
||||||
$prop->NewElement($k, $displayname );
|
$prop->NewElement($k, $displayname );
|
||||||
@ -171,6 +180,6 @@ elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:calendar-multiget"
|
|||||||
include("caldav-REPORT-multiget.php");
|
include("caldav-REPORT-multiget.php");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$request->DoResponse( 501, "XML is not a supported REPORT query document" );
|
$request->DoResponse( 501, "The XML is not a supported REPORT query document" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,7 @@ $tests = array(
|
|||||||
, "20061117T073000" => "RRULE:FREQ=MONTHLY;BYDAY=1MO,2WE,3FR,-1SU"
|
, "20061117T073000" => "RRULE:FREQ=MONTHLY;BYDAY=1MO,2WE,3FR,-1SU"
|
||||||
, "20061107T103000" => "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR"
|
, "20061107T103000" => "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR"
|
||||||
, "20061107T113000" => "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1"
|
, "20061107T113000" => "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1"
|
||||||
|
, "20081020T110000" => "RRULE:FREQ=DAILY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR"
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach( $tests AS $start => $rrule ) {
|
foreach( $tests AS $start => $rrule ) {
|
||||||
@ -26,7 +27,7 @@ foreach( $tests AS $start => $rrule ) {
|
|||||||
$rule = new RRule( new iCalDate($start), $rrule );
|
$rule = new RRule( new iCalDate($start), $rrule );
|
||||||
$i = 0;
|
$i = 0;
|
||||||
do {
|
do {
|
||||||
if ( ($i % 4) == 0 ) echo "\n";
|
if ( ($i % 10) == 0 ) echo "\n";
|
||||||
$date = $rule->GetNext();
|
$date = $rule->GetNext();
|
||||||
if ( isset($date) ) {
|
if ( isset($date) ) {
|
||||||
echo " " . $date->Render();
|
echo " " . $date->Render();
|
||||||
@ -38,4 +39,3 @@ foreach( $tests AS $start => $rrule ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
?>
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user