mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-08-31 18:46:02 +00:00
Add support for writing local scheduling requests on PUT.
In the new Scheduling Extensions for CalDAV the server is expected to construct iCalendar METHOD:REQUEST invitations and put them into the scheduling inbox for each (local) attendee. This patch does that, and hopefully breaks the back of implementing the full scheduling extensions.
This commit is contained in:
parent
7ab6326341
commit
13ce4c147a
@ -18,11 +18,13 @@ unset($session); unset($request); unset($dbconn); unset($_awl_dbconn); unset($in
|
||||
// An ultra-simple exception handler to catch errors that occur
|
||||
// before we get a more functional exception handler in place...
|
||||
function early_exception_handler($e) {
|
||||
echo "Uncaught early exception: ", $e->getMessage(), "\nAt line ", $e->getLine(), " of ", $e->getFile(), "\n";
|
||||
echo "Exception [".$e->getCode()."] ".$e->getmessage()."\n";
|
||||
echo "At line ", $e->getLine(), " of ", $e->getFile(), "\n";
|
||||
echo "================= Stack Trace ===================\n";
|
||||
|
||||
$trace = array_reverse($e->getTrace());
|
||||
foreach( $trace AS $k => $v ) {
|
||||
printf( "=====================================================\n%s[%d] %s%s%s()\n", $v['file'], $v['line'], (isset($v['class'])?$v['class']:''), (isset($v['type'])?$v['type']:''), (isset($v['function'])?$v['function']:'') );
|
||||
printf( "%s[%d] %s%s%s()\n", $v['file'], $v['line'], (isset($v['class'])?$v['class']:''), (isset($v['type'])?$v['type']:''), (isset($v['function'])?$v['function']:'') );
|
||||
}
|
||||
}
|
||||
set_exception_handler('early_exception_handler');
|
||||
@ -75,7 +77,8 @@ $c->dbg = array();
|
||||
if ( ! @include_once('AWLUtilities.php') ) {
|
||||
$try_paths = array(
|
||||
'../../awl/inc'
|
||||
, '/usr/share/awl/inc'
|
||||
, '/usr/share/awl/inc' // Where it ends up on Debian
|
||||
, '/usr/share/php/awl/inc' // Fedora's standard for PHP libraries
|
||||
, '/usr/local/share/awl/inc'
|
||||
);
|
||||
foreach( $try_paths AS $awl_include_path ) {
|
||||
@ -169,7 +172,7 @@ init_gettext( 'davical', $c->locale_path );
|
||||
*
|
||||
*/
|
||||
$c->code_version = 0;
|
||||
$c->want_awl_version = '0.46';
|
||||
$c->want_awl_version = '0.47';
|
||||
$c->version_string = '0.9.9.4'; // The actual version # is replaced into that during the build /release process
|
||||
if ( isset($c->version_string) && preg_match( '/(\d+)\.(\d+)\.(\d+)(.*)/', $c->version_string, $matches) ) {
|
||||
$c->code_major = $matches[1];
|
||||
@ -336,8 +339,9 @@ function ISODateToHTTPDate( $isodate ) {
|
||||
* Convert a date into ISO format into the sparkly new ISO format.
|
||||
* @param string $indate The date to convert
|
||||
*/
|
||||
function DateToISODate( $indate ) {
|
||||
function DateToISODate( $indate, $in_utc=false ) {
|
||||
// Use strtotime since strptime is not available on Windows platform.
|
||||
if ( $in_utc ) return( gmdate('Ymd\THis\Z', strtotime($indate)) );
|
||||
return( date('c', strtotime($indate)) );
|
||||
}
|
||||
|
||||
|
||||
@ -492,11 +492,11 @@ class DAVPrincipal extends Principal
|
||||
break;
|
||||
|
||||
case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
|
||||
$reply->CalDAVElement($prop, 'schedule-inbox-URL', $reply->href($this->url('schedule_inbox')) );
|
||||
$reply->CalDAVElement($prop, 'schedule-inbox-URL', $reply->href($this->url('schedule-inbox')) );
|
||||
break;
|
||||
|
||||
case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
|
||||
$reply->CalDAVElement($prop, 'schedule-outbox-URL', $reply->href($this->url('schedule_outbox')) );
|
||||
$reply->CalDAVElement($prop, 'schedule-outbox-URL', $reply->href($this->url('schedule-outbox')) );
|
||||
break;
|
||||
|
||||
case 'http://calendarserver.org/ns/:dropbox-home-URL':
|
||||
|
||||
@ -467,7 +467,7 @@ EOSQL;
|
||||
/**
|
||||
* Find the collection associated with this resource.
|
||||
*/
|
||||
function FetchCollection() {
|
||||
protected function FetchCollection() {
|
||||
global $session;
|
||||
|
||||
/**
|
||||
@ -536,7 +536,7 @@ EOSQL;
|
||||
if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
|
||||
}
|
||||
else {
|
||||
if ( !isset($this->parent) ) $this->FetchParentContainer();
|
||||
if ( !isset($this->parent) ) $this->GetParentContainer();
|
||||
$this->user_no = $this->parent->GetProperty('user_no');
|
||||
}
|
||||
if ( isset($this->collection->resourcetypes) )
|
||||
@ -554,7 +554,7 @@ EOSQL;
|
||||
/**
|
||||
* Find the principal associated with this resource.
|
||||
*/
|
||||
function FetchPrincipal() {
|
||||
protected function FetchPrincipal() {
|
||||
if ( isset($this->principal) ) return;
|
||||
$this->principal = new DAVPrincipal( array( "path" => $this->bound_from() ) );
|
||||
if ( $this->_is_principal ) {
|
||||
@ -577,7 +577,7 @@ EOSQL;
|
||||
/**
|
||||
* Retrieve the actual resource.
|
||||
*/
|
||||
function FetchResource() {
|
||||
protected function FetchResource() {
|
||||
global $c, $session;
|
||||
|
||||
if ( isset($this->exists) ) return; // True or false, we've got what we can already
|
||||
@ -606,10 +606,12 @@ EOQRY;
|
||||
/**
|
||||
* Fetch any dead properties for this URL
|
||||
*/
|
||||
function FetchDeadProperties() {
|
||||
protected function FetchDeadProperties() {
|
||||
if ( isset($this->dead_properties) ) return;
|
||||
|
||||
$this->dead_properties = array();
|
||||
if ( !$this->exists || !$this->_is_collection ) return;
|
||||
|
||||
$qry = new AwlQuery('SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(':dav_name' => $this->dav_name) );
|
||||
if ( $qry->Exec('DAVResource') ) {
|
||||
while ( $property = $qry->Fetch() ) {
|
||||
@ -619,29 +621,10 @@ EOQRY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetch the parent to this resource.
|
||||
*/
|
||||
function FetchParentContainer() {
|
||||
if ( $this->dav_name == '/' ) return null;
|
||||
if ( !isset($this->parent) ) {
|
||||
if ( $this->_is_collection ) {
|
||||
dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
|
||||
$this->parent = new DAVResource( $this->parent_path() );
|
||||
}
|
||||
else {
|
||||
dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
|
||||
$this->parent = new DAVResource($this->collection->dav_name);
|
||||
}
|
||||
}
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build permissions for this URL
|
||||
*/
|
||||
function FetchPrivileges() {
|
||||
protected function FetchPrivileges() {
|
||||
global $session, $request;
|
||||
|
||||
if ( $this->dav_name == '/' || $this->dav_name == '' ) {
|
||||
@ -666,7 +649,7 @@ EOQRY;
|
||||
if ( ! isset($this->collection) ) $this->FetchCollection();
|
||||
$this->privileges = 0;
|
||||
if ( !isset($this->collection->path_privs) ) {
|
||||
if ( !isset($this->parent) ) $this->FetchParentContainer();
|
||||
if ( !isset($this->parent) ) $this->GetParentContainer();
|
||||
|
||||
$this->collection->path_privs = $this->parent->Privileges();
|
||||
$this->collection->user_no = $this->parent->GetProperty('user_no');
|
||||
@ -696,6 +679,34 @@ EOQRY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a DAVResource which is the parent to this resource.
|
||||
*/
|
||||
function GetParentContainer() {
|
||||
if ( $this->dav_name == '/' ) return null;
|
||||
if ( !isset($this->parent) ) {
|
||||
if ( $this->_is_collection ) {
|
||||
dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
|
||||
$this->parent = new DAVResource( $this->parent_path() );
|
||||
}
|
||||
else {
|
||||
dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
|
||||
$this->parent = new DAVResource($this->collection->dav_name);
|
||||
}
|
||||
}
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetch the parent to this resource.
|
||||
*/
|
||||
function FetchParentContainer() {
|
||||
deprecated('DAVResource::FetchParentContainer');
|
||||
return $this->GetParentContainer();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the privileges bits for the current session user to this resource
|
||||
*/
|
||||
@ -1096,7 +1107,7 @@ EOQRY;
|
||||
if ( $this->collection->dav_name != $this->dav_name ) {
|
||||
return $this->collection->exists;
|
||||
}
|
||||
$parent = $this->FetchParentContainer();
|
||||
$parent = $this->GetParentContainer();
|
||||
return $parent->Exists();
|
||||
}
|
||||
|
||||
@ -1167,6 +1178,24 @@ EOQRY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the internal user_no for the principal for this resource
|
||||
*/
|
||||
function user_no() {
|
||||
if ( !isset($this->principal) ) $this->FetchPrincipal();
|
||||
return $this->principal->user_no();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the internal collection_id for this collection, or the collection containing this resource
|
||||
*/
|
||||
function collection_id() {
|
||||
if ( !isset($this->collection) ) $this->FetchCollection();
|
||||
return $this->collection->collection_id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the database row for this resource
|
||||
*/
|
||||
@ -1321,7 +1350,7 @@ EOQRY;
|
||||
return $this->principal->principal_id();
|
||||
break;
|
||||
|
||||
case 'resourcetype':
|
||||
case 'resourcetype':
|
||||
if ( isset($this->resourcetypes) ) {
|
||||
$this->resourcetypes = preg_replace('{^\s*<(.*)/>\s*$}', '$1', $this->resourcetypes);
|
||||
$type_list = preg_split('{(/>\s*<|\n)}', $this->resourcetypes);
|
||||
@ -1481,13 +1510,13 @@ EOQRY;
|
||||
break;
|
||||
|
||||
case 'DAV::getlastmodified':
|
||||
/** peculiarly, it seems that getlastmodified is HTTP Date format! */
|
||||
/** getlastmodified is HTTP Date format: i.e. the Last-Modified header in response to a GET */
|
||||
$reply->NSElement($prop, $tag, ISODateToHTTPDate($this->GetProperty('modified')) );
|
||||
break;
|
||||
|
||||
case 'DAV::creationdate':
|
||||
/** bizarrely, it seems that creationdate is ISO8601 format */
|
||||
$reply->NSElement($prop, $tag, DateToISODate($this->GetProperty('created')) );
|
||||
/** creationdate is ISO8601 format */
|
||||
$reply->NSElement($prop, $tag, DateToISODate($this->GetProperty('created'), true) );
|
||||
break;
|
||||
|
||||
case 'DAV::getcontentlength':
|
||||
|
||||
@ -381,10 +381,13 @@ class Principal {
|
||||
}
|
||||
|
||||
switch( $type ) {
|
||||
case 'schedule_inbox': $result .= '.in/'; break;
|
||||
case 'schedule_outbox': $result .= '.out/'; break;
|
||||
case 'principal': break;
|
||||
case 'schedule-inbox': $result .= '.in/'; break;
|
||||
case 'schedule-outbox': $result .= '.out/'; break;
|
||||
case 'dropbox': $result .= '.drop/'; break;
|
||||
case 'notifications': $result .= '.notify/'; break;
|
||||
default:
|
||||
fatal('Unknown internal URL type "'.$type.'"');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -11,6 +11,25 @@
|
||||
|
||||
if ( !class_exists('DateTime') ) return;
|
||||
|
||||
/**
|
||||
* Try and extract something like "Pacific/Auckland" or "America/Indiana/Indianapolis" if possible, given
|
||||
* the VTIMEZONE component that is passed in. This is much more complex than olson_from_tzstring since
|
||||
* we start to examine the rules and work out what actual timezone this might be.
|
||||
*/
|
||||
function olson_from_vtimezone( vComponent $vtz ) {
|
||||
$tzid = $vtz->GetProperty('TZID');
|
||||
if ( empty($tzid) ) $tzid = $vtz->GetProperty('TZID');
|
||||
if ( !empty($tzid) ) {
|
||||
$result = olson_from_tzstring($tzid);
|
||||
if ( !empty($result) ) return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo: We'll do other stuff here, in due course...
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
// define( 'DEBUG_RRULE', true);
|
||||
define( 'DEBUG_RRULE', false );
|
||||
|
||||
@ -59,20 +78,24 @@ class Rfc5545Duration {
|
||||
private $epoch_seconds = null;
|
||||
private $days = 0;
|
||||
private $secs = 0;
|
||||
private $as_text = null;
|
||||
private $as_text = '';
|
||||
|
||||
/**
|
||||
* Construct a new Rfc5545Duration either from incoming seconds or a text string.
|
||||
* @param mixed $in_duration
|
||||
*/
|
||||
function __construct( $in_duration ) {
|
||||
if ( preg_match('{^-?P(\dW)|((\dD)?(T(\dH)?(\dM)?(\dS)?)?)$}i', $subject, $matches) ) {
|
||||
if ( is_integer($in_duration) ) {
|
||||
$this->epoch_seconds = $in_duration;
|
||||
$this->as_text = '';
|
||||
}
|
||||
else if ( gettype($in_duration) == 'string' ) {
|
||||
// preg_match('{^-?P(\dW)|((\dD)?(T(\dH)?(\dM)?(\dS)?)?)$}i', $in_duration, $matches) ) {
|
||||
$this->as_text = $in_duration;
|
||||
$this->epoch_seconds = null;
|
||||
}
|
||||
elseif ( is_integer($in_duration) ) {
|
||||
$this->epoch_seconds = $in_duration;
|
||||
$this->as_text = null;
|
||||
else {
|
||||
// fatal('Passed duration is neither numeric nor string!');
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,11 +140,12 @@ class Rfc5545Duration {
|
||||
|
||||
/**
|
||||
* Returns the duration as a text string of the form ^(-?)P(\d+W)|((\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?)$
|
||||
* @return string The stringified stuff.
|
||||
*/
|
||||
function __toString() {
|
||||
if ( !isset($this->as_text) ) {
|
||||
$this->as_text = ($in_duration < 0 ? '-P' : 'P');
|
||||
$in_duration = abs($in_duration);
|
||||
if ( empty($this->as_text) ) {
|
||||
$this->as_text = ($this->epoch_seconds < 0 ? '-P' : 'P');
|
||||
$in_duration = abs($this->epoch_seconds);
|
||||
if ( $in_duration >= 86400 ) {
|
||||
$this->days = floor($in_duration / 86400);
|
||||
$in_duration -= $this->days * 86400;
|
||||
|
||||
@ -5,204 +5,230 @@ class WritableCollection extends DAVResource {
|
||||
|
||||
/**
|
||||
* Writes the data to a member in the collection and returns the segment_name of the resource in our internal namespace.
|
||||
* @param $data iCalendar The resource to be written.
|
||||
* @param $create_resource boolean True if this is a new resource.
|
||||
* @param $segment_name The name of the resource within the collection.
|
||||
* @param vComponent $data The resource to be written.
|
||||
* @param boolean $create_resource True if this is a new resource.
|
||||
* @param $segment_name The name of the resource within the collection, or false on failure.
|
||||
*/
|
||||
function WriteCalendarMember( $data, $create_resource, $segment_name = null ) {
|
||||
if ( !$this->IsSchedulingCollection() && !$this->IsCalendar() ) return false;
|
||||
|
||||
// function write_resource( $user_no, $path, $caldav_data, $collection_id, $author, $etag, $ic, $put_action_type, $caldav_context, $log_action=true, $weak_etag=null ) {
|
||||
global $tz_regex;
|
||||
|
||||
$resources = $ic->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE
|
||||
if ( !isset($resources[0]) ) {
|
||||
$resource_type = 'Unknown';
|
||||
/** @todo Handle writing non-calendar resources, like address book entries or random file data */
|
||||
rollback_on_error( $caldav_context, $user_no, $path, translate('No calendar content'), 412 );
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
$first = $resources[0];
|
||||
$resource_type = $first->GetType();
|
||||
}
|
||||
|
||||
$qry = new AwlQuery();
|
||||
$qry->Begin();
|
||||
$params = array(
|
||||
':dav_name' => $path,
|
||||
':user_no' => $user_no,
|
||||
':etag' => $etag,
|
||||
':dav_data' => $caldav_data,
|
||||
':caldav_type' => $resource_type,
|
||||
':session_user' => $author,
|
||||
':weak_etag' => $weak_etag
|
||||
);
|
||||
if ( $put_action_type == 'INSERT' ) {
|
||||
create_scheduling_requests($vcal);
|
||||
$sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id, weak_etag )
|
||||
VALUES( :user_no, :dav_name, :etag, :dav_data, :caldav_type, :session_user, current_timestamp, current_timestamp, :collection_id, :weak_etag )';
|
||||
$params[':collection_id'] = $collection_id;
|
||||
}
|
||||
else {
|
||||
update_scheduling_requests($vcal);
|
||||
$sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, caldav_type=:caldav_type, logged_user=:session_user,
|
||||
modified=current_timestamp, weak_etag=:weak_etag WHERE user_no=:user_no AND dav_name=:dav_name';
|
||||
}
|
||||
if ( !$qry->QDo($sql,$params) ) {
|
||||
rollback_on_error( $caldav_context, $user_no, $path);
|
||||
return false;
|
||||
}
|
||||
|
||||
$qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path));
|
||||
if ( $qry->rows() == 1 && $row = $qry->Fetch() ) {
|
||||
$dav_id = $row->dav_id;
|
||||
}
|
||||
|
||||
|
||||
$calitem_params = array(
|
||||
':dav_name' => $path,
|
||||
':user_no' => $user_no,
|
||||
':etag' => $etag
|
||||
);
|
||||
$dtstart = $first->GetPValue('DTSTART');
|
||||
$calitem_params[':dtstart'] = $dtstart;
|
||||
if ( (!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '' ) {
|
||||
$dtstart = $first->GetPValue('DUE');
|
||||
}
|
||||
|
||||
$dtend = $first->GetPValue('DTEND');
|
||||
if ( isset($dtend) && $dtend != '' ) {
|
||||
dbg_error_log( 'PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION') );
|
||||
$calitem_params[':dtend'] = $dtend;
|
||||
$dtend = ':dtend';
|
||||
}
|
||||
else {
|
||||
$dtend = 'NULL';
|
||||
if ( $first->GetPValue('DURATION') != '' AND $dtstart != '' ) {
|
||||
$duration = preg_replace( '#[PT]#', ' ', $first->GetPValue('DURATION') );
|
||||
$dtend = '(:dtstart::timestamp with time zone + :duration::interval)';
|
||||
$calitem_params[':duration'] = $duration;
|
||||
function WriteCalendarMember( vComponent $data, $create_resource, $do_scheduling=true, $segment_name = null ) {
|
||||
if ( !$this->IsSchedulingCollection() && !$this->IsCalendar() ) {
|
||||
dbg_error_log( 'PUT', '"%s" is not a calendar or scheduling collection!', $this->dav_name);
|
||||
return false;
|
||||
}
|
||||
elseif ( $first->GetType() == 'VEVENT' ) {
|
||||
/**
|
||||
* From RFC2445 4.6.1:
|
||||
* For cases where a "VEVENT" calendar component specifies a "DTSTART"
|
||||
* property with a DATE data type but no "DTEND" property, the events
|
||||
* non-inclusive end is the end of the calendar date specified by the
|
||||
* "DTSTART" property. For cases where a "VEVENT" calendar component specifies
|
||||
* a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
|
||||
* the event ends on the same calendar date and time of day specified by the
|
||||
* "DTSTART" property.
|
||||
*
|
||||
* So we're looking for 'VALUE=DATE', to identify the duration, effectively.
|
||||
*
|
||||
*/
|
||||
$value_type = $first->GetPParamValue('DTSTART','VALUE');
|
||||
dbg_error_log('PUT','DTSTART without DTEND. DTSTART value type is %s', $value_type );
|
||||
if ( isset($value_type) && $value_type == 'DATE' )
|
||||
$dtend = '(:dtstart::timestamp with time zone::date + \'1 day\'::interval)';
|
||||
else
|
||||
$dtend = ':dtstart';
|
||||
}
|
||||
}
|
||||
|
||||
$last_modified = $first->GetPValue('LAST-MODIFIED');
|
||||
if ( !isset($last_modified) || $last_modified == '' ) {
|
||||
$last_modified = gmdate( 'Ymd\THis\Z' );
|
||||
}
|
||||
$calitem_params[':modified'] = $last_modified;
|
||||
global $tz_regex, $session, $caldav_context;
|
||||
|
||||
$resources = $data->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE
|
||||
$user_no = $this->user_no();
|
||||
$collection_id = $this->collection_id();
|
||||
|
||||
$dtstamp = $first->GetPValue('DTSTAMP');
|
||||
if ( !isset($dtstamp) || $dtstamp == '' ) {
|
||||
$dtstamp = $last_modified;
|
||||
}
|
||||
$calitem_params[':dtstamp'] = $dtstamp;
|
||||
|
||||
$class = $first->GetPValue('CLASS');
|
||||
/* Check and see if we should over ride the class. */
|
||||
/** @todo is there some way we can move this out of this function? Or at least get rid of the need for the SQL query here. */
|
||||
if ( public_events_only($user_no, $path) ) {
|
||||
$class = 'PUBLIC';
|
||||
}
|
||||
|
||||
/*
|
||||
* It seems that some calendar clients don't set a class...
|
||||
* RFC2445, 4.8.1.3:
|
||||
* Default is PUBLIC
|
||||
*/
|
||||
if ( !isset($class) || $class == '' ) {
|
||||
$class = 'PUBLIC';
|
||||
}
|
||||
$calitem_params[':class'] = $class;
|
||||
|
||||
|
||||
/** Calculate what timezone to set, first, if possible */
|
||||
$last_tz_locn = 'Turkmenikikamukau'; // I really hope this location doesn't exist!
|
||||
$tzid = $first->GetPParamValue('DTSTART','TZID');
|
||||
if ( !isset($tzid) || $tzid == '' ) $tzid = $first->GetPParamValue('DUE','TZID');
|
||||
$timezones = $ic->GetComponents('VTIMEZONE');
|
||||
foreach( $timezones AS $k => $tz ) {
|
||||
if ( $tz->GetPValue('TZID') != $tzid ) {
|
||||
/**
|
||||
* We'll pretend they didn't forget to give us a TZID and that they
|
||||
* really hope the server is running in the timezone they supplied... but be noisy about it.
|
||||
*/
|
||||
dbg_error_log( 'ERROR', ' Event includes TZID[%s] but uses TZID[%s]!', $tz->GetPValue('TZID'), $tzid );
|
||||
$tzid = $tz->GetPValue('TZID');
|
||||
}
|
||||
// This is the one
|
||||
$tz_locn = $tz->GetPValue('X-LIC-LOCATION');
|
||||
if ( ! isset($tz_locn) ) {
|
||||
if ( preg_match( '#([^/]+/[^/]+)$#', $tzid, $matches ) )
|
||||
$tz_locn = $matches[1];
|
||||
else if ( isset($tzid) && $tzid != '' ) {
|
||||
dbg_error_log( 'ERROR', ' Couldn\'t guess Olsen TZ from TZID[%s]. This may end in tears...', $tzid );
|
||||
}
|
||||
if ( !isset($resources[0]) ) {
|
||||
rollback_on_error( $caldav_context, $user_no, $this->dav_name.'/'.$segment_name, translate('No calendar content'), 412 );
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if ( ! preg_match( $tz_regex, $tz_locn ) ) {
|
||||
if ( preg_match( '#([^/]+/[^/]+)$#', $tzid, $matches ) ) $tz_locn = $matches[1];
|
||||
$first = $resources[0];
|
||||
$resource_type = $first->GetType();
|
||||
}
|
||||
|
||||
$uid = $first->GetPValue('UID');
|
||||
if ( empty($segment_name) ) {
|
||||
$segment_name = $uid.'.ics';
|
||||
}
|
||||
$path = $this->dav_name() . $segment_name;
|
||||
|
||||
$caldav_data = $data->Render();
|
||||
$etag = md5($caldav_data);
|
||||
$weak_etag = null;
|
||||
|
||||
$qry = new AwlQuery();
|
||||
$existing_transaction_state = $qry->TransactionState();
|
||||
if ( $existing_transaction_state == 0 ) $qry->Begin();
|
||||
|
||||
|
||||
if ( $create_resource ) {
|
||||
$qry->QDo('SELECT nextval(\'dav_id_seq\') AS dav_id');
|
||||
}
|
||||
else {
|
||||
$qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path));
|
||||
}
|
||||
if ( $qry->rows() != 1 || !($row = $qry->Fetch()) ) {
|
||||
// No dav_id? => We're toast!
|
||||
rollback_on_error( $caldav_context, $user_no, $path);
|
||||
return false;
|
||||
}
|
||||
$dav_id = $row->dav_id;
|
||||
|
||||
$calitem_params = array(
|
||||
':dav_name' => $path,
|
||||
':user_no' => $user_no,
|
||||
':etag' => $etag,
|
||||
':dav_id' => $dav_id
|
||||
);
|
||||
|
||||
$dav_params = array_merge($calitem_params, array(
|
||||
':dav_data' => $caldav_data,
|
||||
':caldav_type' => $resource_type,
|
||||
':session_user' => $session->user_no,
|
||||
':weak_etag' => $weak_etag
|
||||
) );
|
||||
|
||||
if ( $create_resource ) {
|
||||
if ( !$this->IsSchedulingCollection() ) create_scheduling_requests($vcal);
|
||||
$sql = 'INSERT INTO caldav_data ( dav_id, user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id, weak_etag )
|
||||
VALUES( :dav_id, :user_no, :dav_name, :etag, :dav_data, :caldav_type, :session_user, current_timestamp, current_timestamp, :collection_id, :weak_etag )';
|
||||
$dav_params[':collection_id'] = $collection_id;
|
||||
}
|
||||
else {
|
||||
if ( !$this->IsSchedulingCollection() ) update_scheduling_requests($vcal);
|
||||
$sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, caldav_type=:caldav_type, logged_user=:session_user,
|
||||
modified=current_timestamp, weak_etag=:weak_etag WHERE dav_id=:dav_id';
|
||||
}
|
||||
if ( !$qry->QDo($sql,$dav_params) ) {
|
||||
rollback_on_error( $caldav_context, $user_no, $path);
|
||||
return false;
|
||||
}
|
||||
|
||||
$dtstart = $first->GetPValue('DTSTART');
|
||||
$calitem_params[':dtstart'] = $dtstart;
|
||||
if ( (!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '' ) {
|
||||
$dtstart = $first->GetPValue('DUE');
|
||||
}
|
||||
|
||||
$dtend = $first->GetPValue('DTEND');
|
||||
if ( isset($dtend) && $dtend != '' ) {
|
||||
dbg_error_log( 'PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION') );
|
||||
$calitem_params[':dtend'] = $dtend;
|
||||
$dtend = ':dtend';
|
||||
}
|
||||
else {
|
||||
$dtend = 'NULL';
|
||||
if ( $first->GetPValue('DURATION') != '' AND $dtstart != '' ) {
|
||||
$duration = preg_replace( '#[PT]#', ' ', $first->GetPValue('DURATION') );
|
||||
$dtend = '(:dtstart::timestamp with time zone + :duration::interval)';
|
||||
$calitem_params[':duration'] = $duration;
|
||||
}
|
||||
elseif ( $first->GetType() == 'VEVENT' ) {
|
||||
/**
|
||||
* From RFC2445 4.6.1:
|
||||
* For cases where a "VEVENT" calendar component specifies a "DTSTART"
|
||||
* property with a DATE data type but no "DTEND" property, the events
|
||||
* non-inclusive end is the end of the calendar date specified by the
|
||||
* "DTSTART" property. For cases where a "VEVENT" calendar component specifies
|
||||
* a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
|
||||
* the event ends on the same calendar date and time of day specified by the
|
||||
* "DTSTART" property.
|
||||
*
|
||||
* So we're looking for 'VALUE=DATE', to identify the duration, effectively.
|
||||
*
|
||||
*/
|
||||
$value_type = $first->GetPParamValue('DTSTART','VALUE');
|
||||
dbg_error_log('PUT','DTSTART without DTEND. DTSTART value type is %s', $value_type );
|
||||
if ( isset($value_type) && $value_type == 'DATE' )
|
||||
$dtend = '(:dtstart::timestamp with time zone::date + \'1 day\'::interval)';
|
||||
else
|
||||
$dtend = ':dtstart';
|
||||
}
|
||||
}
|
||||
|
||||
dbg_error_log( 'PUT', ' Using TZID[%s] and location of [%s]', $tzid, (isset($tz_locn) ? $tz_locn : '') );
|
||||
if ( isset($tz_locn) && ($tz_locn != $last_tz_locn) && preg_match( $tz_regex, $tz_locn ) ) {
|
||||
dbg_error_log( 'PUT', ' Setting timezone to %s', $tz_locn );
|
||||
if ( $tz_locn != '' ) {
|
||||
$qry->QDo('SET TIMEZONE TO \''.$tz_locn."'" );
|
||||
|
||||
$last_modified = $first->GetPValue('LAST-MODIFIED');
|
||||
if ( !isset($last_modified) || $last_modified == '' ) {
|
||||
$last_modified = gmdate( 'Ymd\THis\Z' );
|
||||
}
|
||||
$calitem_params[':modified'] = $last_modified;
|
||||
|
||||
$dtstamp = $first->GetPValue('DTSTAMP');
|
||||
if ( !isset($dtstamp) || $dtstamp == '' ) {
|
||||
$dtstamp = $last_modified;
|
||||
}
|
||||
$calitem_params[':dtstamp'] = $dtstamp;
|
||||
|
||||
$class = $first->GetPValue('CLASS');
|
||||
/* Check and see if we should over ride the class. */
|
||||
/** @todo is there some way we can move this out of this function? Or at least get rid of the need for the SQL query here. */
|
||||
if ( public_events_only($user_no, $path) ) {
|
||||
$class = 'PUBLIC';
|
||||
}
|
||||
|
||||
/*
|
||||
* It seems that some calendar clients don't set a class...
|
||||
* RFC2445, 4.8.1.3:
|
||||
* Default is PUBLIC
|
||||
*/
|
||||
if ( !isset($class) || $class == '' ) {
|
||||
$class = 'PUBLIC';
|
||||
}
|
||||
$calitem_params[':class'] = $class;
|
||||
|
||||
|
||||
/** Calculate what timezone to set, first, if possible */
|
||||
$last_tz_locn = 'Turkmenikikamukau'; // I really hope this location doesn't exist!
|
||||
$dtstart_prop = $first->GetProperty('DTSTART');
|
||||
$tzid = $dtstart_prop->GetParameterValue('TZID');
|
||||
if ( empty($tzid) && $first->GetType() == 'VTODO' ) {
|
||||
$due_prop = $first->GetProperty('DUE');
|
||||
$tzid = $due_prop->GetParameterValue('TZID');
|
||||
}
|
||||
$timezones = $data->GetComponents('VTIMEZONE');
|
||||
foreach( $timezones AS $k => $tz ) {
|
||||
if ( $tz->GetPValue('TZID') != $tzid ) {
|
||||
/**
|
||||
* We'll skip any tz definitions that are for a TZID other than the DTSTART/DUE on the first VEVENT/VTODO
|
||||
*/
|
||||
dbg_error_log( 'ERROR', ' Event uses TZID[%s], skipping included TZID[%s]!', $tz->GetPValue('TZID'), $tzid );
|
||||
continue;
|
||||
}
|
||||
$last_tz_locn = $tz_locn;
|
||||
// This is the one
|
||||
$tz_locn = $tz->GetPValue('X-LIC-LOCATION');
|
||||
if ( ! isset($tz_locn) ) {
|
||||
if ( preg_match( '#([^/]+/[^/]+)$#', $tzid, $matches ) )
|
||||
$tz_locn = $matches[1];
|
||||
else if ( isset($tzid) && $tzid != '' ) {
|
||||
dbg_error_log( 'ERROR', ' Couldn\'t guess Olsen TZ from TZID[%s]. This may end in tears...', $tzid );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( ! preg_match( $tz_regex, $tz_locn ) ) {
|
||||
if ( preg_match( '#([^/]+/[^/]+)$#', $tzid, $matches ) ) $tz_locn = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
dbg_error_log( 'PUT', ' Using TZID[%s] and location of [%s]', $tzid, (isset($tz_locn) ? $tz_locn : '') );
|
||||
if ( isset($tz_locn) && ($tz_locn != $last_tz_locn) && preg_match( $tz_regex, $tz_locn ) ) {
|
||||
dbg_error_log( 'PUT', ' Setting timezone to %s', $tz_locn );
|
||||
if ( $tz_locn != '' ) {
|
||||
$qry->QDo('SET TIMEZONE TO \''.$tz_locn."'" );
|
||||
}
|
||||
$last_tz_locn = $tz_locn;
|
||||
}
|
||||
$params = array( ':tzid' => $tzid);
|
||||
$qry = new AwlQuery('SELECT tz_locn FROM time_zone WHERE tz_id = :tzid', $params );
|
||||
if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 0 ) {
|
||||
$params[':tzlocn'] = $tz_locn;
|
||||
$params[':tzspec'] = (isset($tz) ? $tz->Render() : null );
|
||||
$qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params );
|
||||
}
|
||||
if ( !isset($tz_locn) || $tz_locn == '' ) $tz_locn = $tzid;
|
||||
|
||||
}
|
||||
$params = array( ':tzid' => $tzid);
|
||||
$qry = new AwlQuery('SELECT tz_locn FROM time_zone WHERE tz_id = :tzid', $params );
|
||||
if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 0 ) {
|
||||
$params[':tzlocn'] = $tz_locn;
|
||||
$params[':tzspec'] = (isset($tz) ? $tz->Render() : null );
|
||||
$qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params );
|
||||
}
|
||||
if ( !isset($tz_locn) || $tz_locn == '' ) $tz_locn = $tzid;
|
||||
|
||||
}
|
||||
|
||||
$created = $first->GetPValue('CREATED');
|
||||
if ( $created == '00001231T000000Z' ) $created = '20001231T000000Z';
|
||||
$calitem_params[':created'] = $created;
|
||||
|
||||
$calitem_params[':tzid'] = $tzid;
|
||||
$calitem_params[':uid'] = $first->GetPValue('UID');
|
||||
$calitem_params[':summary'] = $first->GetPValue('SUMMARY');
|
||||
$calitem_params[':location'] = $first->GetPValue('LOCATION');
|
||||
$calitem_params[':transp'] = $first->GetPValue('TRANSP');
|
||||
$calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
|
||||
$calitem_params[':rrule'] = $first->GetPValue('RRULE');
|
||||
$calitem_params[':url'] = $first->GetPValue('URL');
|
||||
$calitem_params[':priority'] = $first->GetPValue('PRIORITY');
|
||||
$calitem_params[':due'] = $first->GetPValue('DUE');
|
||||
$calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
|
||||
$calitem_params[':status'] = $first->GetPValue('STATUS');
|
||||
if ( $put_action_type == 'INSERT' ) {
|
||||
$sql = <<<EOSQL
|
||||
|
||||
$created = $first->GetPValue('CREATED');
|
||||
if ( $created == '00001231T000000Z' ) $created = '20001231T000000Z';
|
||||
$calitem_params[':created'] = $created;
|
||||
|
||||
$calitem_params[':tzid'] = $tzid;
|
||||
$calitem_params[':uid'] = $uid;
|
||||
$calitem_params[':summary'] = $first->GetPValue('SUMMARY');
|
||||
$calitem_params[':location'] = $first->GetPValue('LOCATION');
|
||||
$calitem_params[':transp'] = $first->GetPValue('TRANSP');
|
||||
$calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
|
||||
$calitem_params[':rrule'] = $first->GetPValue('RRULE');
|
||||
$calitem_params[':url'] = $first->GetPValue('URL');
|
||||
$calitem_params[':priority'] = $first->GetPValue('PRIORITY');
|
||||
$calitem_params[':due'] = $first->GetPValue('DUE');
|
||||
$calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
|
||||
$calitem_params[':status'] = $first->GetPValue('STATUS');
|
||||
if ( $create_resource ) {
|
||||
$sql = <<<EOSQL
|
||||
INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp,
|
||||
dtstart, dtend, summary, location, class, transp,
|
||||
description, rrule, tz_id, last_modified, url, priority,
|
||||
@ -212,40 +238,41 @@ INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp,
|
||||
:description, :rrule, :tzid, :modified, :url, :priority,
|
||||
:created, :due, :percent_complete, :status, $collection_id )
|
||||
EOSQL;
|
||||
$sync_change = 201;
|
||||
}
|
||||
else {
|
||||
$sql = <<<EOSQL
|
||||
$sync_change = 201;
|
||||
}
|
||||
else {
|
||||
$sql = <<<EOSQL
|
||||
UPDATE calendar_item SET dav_etag=:etag, uid=:uid, dtstamp=:dtstamp,
|
||||
dtstart=:dtstart, dtend=$dtend, summary=:summary, location=:location, class=:class, transp=:transp,
|
||||
description=:description, rrule=:rrule, tz_id=:tzid, last_modified=:modified, url=:url, priority=:priority,
|
||||
created=:created, due=:due, percent_complete=:percent_complete, status=:status
|
||||
WHERE user_no=:user_no AND dav_name=:dav_name
|
||||
EOSQL;
|
||||
$sync_change = 200;
|
||||
}
|
||||
|
||||
write_alarms($dav_id, $first);
|
||||
write_attendees($dav_id, $first);
|
||||
|
||||
if ( $log_action && function_exists('log_caldav_action') ) {
|
||||
log_caldav_action( $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
|
||||
}
|
||||
else if ( $log_action ) {
|
||||
dbg_error_log( 'PUT', 'No log_caldav_action( %s, %s, %s, %s, %s) can be called.',
|
||||
$put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
|
||||
}
|
||||
$sync_change = 200;
|
||||
}
|
||||
|
||||
$qry = new AwlQuery( $sql, $calitem_params );
|
||||
if ( !$qry->Exec('PUT',__LINE__,__FILE__) ) {
|
||||
rollback_on_error( $caldav_context, $user_no, $path);
|
||||
return false;
|
||||
}
|
||||
$qry->QDo("SELECT write_sync_change( $collection_id, $sync_change, :dav_name)", array(':dav_name' => $path ) );
|
||||
$qry->Commit();
|
||||
|
||||
dbg_error_log( 'PUT', 'User: %d, ETag: %s, Path: %s', $author, $etag, $path);
|
||||
if ( !$this->IsSchedulingCollection() ) {
|
||||
write_alarms($dav_id, $first);
|
||||
write_attendees($dav_id, $first);
|
||||
if ( $log_action && function_exists('log_caldav_action') ) {
|
||||
log_caldav_action( $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
|
||||
}
|
||||
else if ( $log_action ) {
|
||||
dbg_error_log( 'PUT', 'No log_caldav_action( %s, %s, %s, %s, %s) can be called.',
|
||||
$put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
|
||||
}
|
||||
}
|
||||
|
||||
$qry = new AwlQuery( $sql, $calitem_params );
|
||||
if ( !$qry->Exec('PUT',__LINE__,__FILE__) ) {
|
||||
rollback_on_error( $caldav_context, $user_no, $path);
|
||||
return false;
|
||||
}
|
||||
$qry->QDo("SELECT write_sync_change( $collection_id, $sync_change, :dav_name)", array(':dav_name' => $path ) );
|
||||
if ( $existing_transaction_state == 0 ) $qry->Commit();
|
||||
|
||||
dbg_error_log( 'PUT', 'User: %d, ETag: %s, Path: %s', $session->user_no, $etag, $path);
|
||||
|
||||
|
||||
return $segment_name;
|
||||
}
|
||||
@ -254,14 +281,22 @@ EOSQL;
|
||||
* Writes the data to a member in the collection and returns the segment_name of the resource in our internal namespace.
|
||||
* @param $data mixed The resource to be written.
|
||||
* @param $create_resource boolean True if this is a new resource.
|
||||
* @param $segment_name The name of the resource within the collection.
|
||||
* @param $segment_name The name of the resource within the collection, or false on failure.
|
||||
*/
|
||||
function WriteMember( $data, $create_resource, $segment_name = null ) {
|
||||
if ( ! $this->IsCollection() ) return false;
|
||||
if ( is_object($data) ) {
|
||||
if ( gettype($data) == 'iCalendar' ) return $this->WriteCalendarMember($data,$create_resource,$segment_name);
|
||||
else if ( gettype($data) == 'VCard' ) return $this->WriteAddressbookMember($data,$create_resource,$segment_name);
|
||||
if ( ! $this->IsCollection() ) {
|
||||
dbg_error_log( 'PUT', '"%s" is not a collection path', $this->dav_name);
|
||||
return false;
|
||||
}
|
||||
if ( ! is_object($data) ) {
|
||||
dbg_error_log( 'PUT', 'No data supplied!' );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $data instanceof vComponent )
|
||||
return $this->WriteCalendarMember($data,$create_resource,$segment_name);
|
||||
else if ( $data instanceof VCard )
|
||||
return $this->WriteAddressbookMember($data,$create_resource,$segment_name);
|
||||
|
||||
return $segment_name;
|
||||
}
|
||||
|
||||
@ -18,11 +18,13 @@ unset($session); unset($request); unset($dbconn); unset($_awl_dbconn); unset($in
|
||||
// An ultra-simple exception handler to catch errors that occur
|
||||
// before we get a more functional exception handler in place...
|
||||
function early_exception_handler($e) {
|
||||
echo "Uncaught early exception: ", $e->getMessage(), "\nAt line ", $e->getLine(), " of ", $e->getFile(), "\n";
|
||||
echo "Exception [".$e->getCode()."] ".$e->getmessage()."\n";
|
||||
echo "At line ", $e->getLine(), " of ", $e->getFile(), "\n";
|
||||
echo "================= Stack Trace ===================\n";
|
||||
|
||||
$trace = array_reverse($e->getTrace());
|
||||
foreach( $trace AS $k => $v ) {
|
||||
printf( "=====================================================\n%s[%d] %s%s%s()\n", $v['file'], $v['line'], (isset($v['class'])?$v['class']:''), (isset($v['type'])?$v['type']:''), (isset($v['function'])?$v['function']:'') );
|
||||
printf( "%s[%d] %s%s%s()\n", $v['file'], $v['line'], (isset($v['class'])?$v['class']:''), (isset($v['type'])?$v['type']:''), (isset($v['function'])?$v['function']:'') );
|
||||
}
|
||||
}
|
||||
set_exception_handler('early_exception_handler');
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
dbg_error_log("POST", "method handler");
|
||||
|
||||
require_once("XMLDocument.php");
|
||||
require_once("iCalendar.php");
|
||||
include_once('caldav-PUT-functions.php');
|
||||
include_once('freebusy-functions.php');
|
||||
|
||||
@ -97,8 +96,7 @@ function handle_freebusy_request( $ic ) {
|
||||
$fb->SetProperties( $ic->GetProperties('ORGANIZER'), 'ORGANIZER');
|
||||
$fb->AddProperty( $attendee );
|
||||
|
||||
$vcal = new iCalComponent();
|
||||
$vcal->VCalendar( array('METHOD' => 'REPLY') );
|
||||
$vcal = new vCalendar( array('METHOD' => 'REPLY') );
|
||||
$vcal->AddComponent( $fb );
|
||||
|
||||
$response = $reply->NewXMLElement( "response", false, false, 'urn:ietf:params:xml:ns:caldav' );
|
||||
@ -124,7 +122,7 @@ function handle_cancel_request( $ic ) {
|
||||
$request->XMLResponse( 200, $response );
|
||||
}
|
||||
|
||||
$ical = new iCalComponent( $request->raw_post );
|
||||
$ical = new vComponent( $request->raw_post );
|
||||
$method = $ical->GetPValue('METHOD');
|
||||
|
||||
$resources = $ical->GetComponents('VTIMEZONE',false);
|
||||
|
||||
@ -91,7 +91,6 @@ function add_proxy_response( $which, $parent_path ) {
|
||||
$collection->description = '';
|
||||
|
||||
$resource = new DAVResource($collection);
|
||||
$resource->FetchPrincipal();
|
||||
return $resource->RenderAsXML($property_list, $reply);
|
||||
|
||||
}
|
||||
|
||||
@ -16,7 +16,8 @@
|
||||
*/
|
||||
|
||||
require_once('AwlCache.php');
|
||||
require_once('iCalendar.php');
|
||||
require_once('vComponent.php');
|
||||
require_once('vCalendar.php');
|
||||
require_once('WritableCollection.php');
|
||||
|
||||
$bad_events = null;
|
||||
@ -159,10 +160,23 @@ function public_events_only( $user_no, $dav_name ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a TZID string from this VEVENT/VTODO/... component if we can
|
||||
* @param vComponent $comp
|
||||
* @return The TZID value we found, or null
|
||||
*/
|
||||
function GetTZID( vComponent $comp ) {
|
||||
$p = $comp->GetProperty('DTSTART');
|
||||
if ( !isset($p) && $comp->GetType() == 'VTODO' ) {
|
||||
$p = $comp->GetProperty('DUE');
|
||||
}
|
||||
if ( !isset($p) ) return null;
|
||||
return $p->GetParameterValue('TZID');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deliver scheduling requests to attendees
|
||||
* @param iCalComponent $ical the VCALENDAR to deliver
|
||||
* @param vComponent $ical the VCALENDAR to deliver
|
||||
*/
|
||||
function handle_schedule_request( $ical ) {
|
||||
global $c, $session, $request;
|
||||
@ -207,7 +221,7 @@ function handle_schedule_request( $ical ) {
|
||||
$attendee->SetParameterValue ('SCHEDULE-STATUS','5.3;No scheduling support for user');
|
||||
continue;
|
||||
}
|
||||
$deliver_path = $attendee_principal->internal_url('schedule_inbox');
|
||||
$deliver_path = $attendee_principal->internal_url('schedule-inbox');
|
||||
|
||||
$ar = new DAVResource($deliver_path);
|
||||
$priv = $ar->HavePrivilegeTo('schedule-deliver-invite' );
|
||||
@ -223,29 +237,23 @@ function handle_schedule_request( $ical ) {
|
||||
|
||||
|
||||
$attendee->SetParameterValue ('SCHEDULE-STATUS','1.2;Scheduling message has been delivered');
|
||||
$ncal = new iCalComponent ( );
|
||||
$ncal->VCalendar ();
|
||||
$ncal->AddProperty ( 'METHOD', 'REQUEST' );
|
||||
$ncal = new vCalendar( array('METHOD' => 'REQUEST') );
|
||||
$ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
|
||||
$content = $ncal->Render();
|
||||
$cid = $ar->GetProperty('collection_id');
|
||||
dbg_error_log('DELIVER', 'to user: %s, to path: %s, collection: %s, from user: %s, caldata %s', $attendee_principal->user_no(), $deliver_path, $cid, $request->user_no, $content );
|
||||
write_resource( $attendee_principal->user_no(), $deliver_path . $etag . '.ics' ,
|
||||
$content , $ar->GetProperty('collection_id'), $request->user_no,
|
||||
md5($content), $ncal, $put_action_type='INSERT', $caldav_context=true, $log_action=true, $etag );
|
||||
$attendee->SetParameterValue ('SCHEDULE-STATUS','1.2;Scheduling message has been delivered');
|
||||
write_resource( $deliver_path . $etag . '.ics', $content, $ar, $request->user_no,
|
||||
md5($content), $ncal, $put_action_type='INSERT', $caldav_context=true, $log_action=true, $etag );
|
||||
$attendee->SetParameterValue ('SCHEDULE-STATUS','1.2;Scheduling message has been delivered');
|
||||
}
|
||||
// don't write an entry in the out box, ical doesn't delete it or ever read it again
|
||||
$ncal = new iCalComponent ( );
|
||||
$ncal->VCalendar ();
|
||||
$ncal->AddProperty ( 'METHOD', 'REQUEST' );
|
||||
$ncal = new vCalendar(array('METHOD' => 'REQUEST'));
|
||||
$ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
|
||||
$content = $ncal->Render();
|
||||
$deliver_path = $request->principal->internal_url('schedule_inbox');
|
||||
$deliver_path = $request->principal->internal_url('schedule-inbox');
|
||||
$ar = new DAVResource($deliver_path);
|
||||
write_resource( $request->user_no, $deliver_path . $etag . '.ics' ,
|
||||
$content , $ar->GetProperty('collection_id'), $request->user_no,
|
||||
md5($content), $ncal, $put_action_type='INSERT', $caldav_context=true, $log_action=true, $etag );
|
||||
write_resource( $deliver_path . $etag . '.ics', $content, $ar, $request->user_no, md5($content),
|
||||
$ncal, $put_action_type='INSERT', $caldav_context=true, $log_action=true, $etag );
|
||||
//$etag = md5($content);
|
||||
header('ETag: "'. $etag . '"' );
|
||||
header('Schedule-Tag: "'.$etag . '"' );
|
||||
@ -254,10 +262,10 @@ function handle_schedule_request( $ical ) {
|
||||
|
||||
/**
|
||||
* Deliver scheduling replies to organizer and other attendees
|
||||
* @param iCalComponent $ical the VCALENDAR to deliver
|
||||
* @param vComponent $ical the VCALENDAR to deliver
|
||||
* @return false on error
|
||||
*/
|
||||
function handle_schedule_reply ( $ical ) {
|
||||
function handle_schedule_reply ( vComponent $ical ) {
|
||||
global $c, $session, $request;
|
||||
$resources = $ical->GetComponents('VTIMEZONE',false);
|
||||
$ic = $resources[0];
|
||||
@ -298,14 +306,11 @@ function handle_schedule_reply ( $ical ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ncal = new iCalComponent ( );
|
||||
$ncal->VCalendar ();
|
||||
$ncal->AddProperty ( 'METHOD', 'REPLY' );
|
||||
$ncal = new vCalendar( array('METHOD' => 'REPLY') );
|
||||
$ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
|
||||
$content = $ncal->Render();
|
||||
write_resource( $attendee_principal->user_no(), $deliver_path . $etag . '.ics' ,
|
||||
$content , $ar->GetProperty('collection_id'), $request->user_no,
|
||||
md5($content), $ncal, $put_action_type='INSERT', $caldav_context=true, $log_action=true, $etag );
|
||||
write_resource( $deliver_path . $etag . '.ics', $content, $ar, $request->user_no, md5($content),
|
||||
$ncal, $put_action_type='INSERT', $caldav_context=true, $log_action=true, $etag );
|
||||
}
|
||||
$request->DoResponse( 201, 'Created' );
|
||||
}
|
||||
@ -315,23 +320,23 @@ function handle_schedule_reply ( $ical ) {
|
||||
|
||||
/**
|
||||
* Create a scheduling request in the schedule inbox for the
|
||||
* @param iCalComponent $resource The VEVENT/VTODO/... resource we are scheduling
|
||||
* @param iCalProp $attendee The attendee we are scheduling
|
||||
* @param vComponent $resource The VEVENT/VTODO/... resource we are scheduling
|
||||
* @param vProperty $attendee The attendee we are scheduling
|
||||
* @return float The result of the scheduling request, per caldav-sched #3.5.4
|
||||
*/
|
||||
function write_scheduling_request( &$resource, $attendee_value, $create_resource ) {
|
||||
$email = preg_replace( '/^mailto:/', '', $attendee_value );
|
||||
function write_scheduling_request( vComponent $resource, $attendee_value, $create_resource ) {
|
||||
$email = preg_replace( '/^mailto:/i', '', $attendee_value );
|
||||
$schedule_target = new Principal('email',$email);
|
||||
if ( $schedule_target->Exists() ) {
|
||||
$attendee_inbox = new WritableCollection(array('path' => $schedule_target->internal_url('schedule-inbox')));
|
||||
if ( ! $attendee_inbox->HavePrivilegeTo('schedule-deliver-invite') ) {
|
||||
$response = '3.8;'.translate('No authority to deliver invitations to user.');
|
||||
}
|
||||
else if ( $attendee_inbox->WriteCalendarMember($resource, $create_resource) ) {
|
||||
$response = '2.0;'.translate('Scheduling invitation delivered successfully');
|
||||
else if ( $attendee_inbox->WriteCalendarMember($resource, $create_resource) === false ) {
|
||||
$response = '5.3;'.translate('No scheduling support for user');
|
||||
}
|
||||
else {
|
||||
$response = '5.3;'.translate('No scheduling support for user');
|
||||
$response = '2.0;'.translate('Scheduling invitation delivered successfully');
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -342,9 +347,9 @@ function write_scheduling_request( &$resource, $attendee_value, $create_resource
|
||||
|
||||
/**
|
||||
* Create scheduling requests in the schedule inbox for the
|
||||
* @param iCalComponent $resource The VEVENT/VTODO/... resource we are scheduling
|
||||
* @param vComponent $resource The VEVENT/VTODO/... resource we are scheduling
|
||||
*/
|
||||
function create_scheduling_requests( &$resource ) {
|
||||
function create_scheduling_requests( vComponent $resource ) {
|
||||
if ( ! is_object($resource) ) {
|
||||
dbg_error_log( 'PUT', 'create_scheduling_requests called with non-object parameter (%s)', gettype($resource) );
|
||||
return;
|
||||
@ -365,16 +370,18 @@ function create_scheduling_requests( &$resource ) {
|
||||
|
||||
dbg_error_log( 'PUT', 'Adding to scheduling inbox %d attendees', count($attendees) );
|
||||
foreach( $attendees AS $attendee ) {
|
||||
$attendee->SetParameterValue( 'SCHEDULE-STATUS', write_scheduling_request( $resource, $attendee->Value(), true ) );
|
||||
$schedule_status = write_scheduling_request( $resource, $attendee->Value(), true );
|
||||
dbg_error_log( 'PUT', 'Status for attendee <%s> set to "%s"', $attendee->Value(), $schedule_status );
|
||||
$attendee->SetParameterValue( 'SCHEDULE-STATUS', $schedule_status );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update scheduling requests in the schedule inbox for the
|
||||
* @param iCalComponent $resource The VEVENT/VTODO/... resource we are scheduling
|
||||
* @param vComponent $resource The VEVENT/VTODO/... resource we are scheduling
|
||||
*/
|
||||
function update_scheduling_requests( &$resource ) {
|
||||
function update_scheduling_requests( vComponent $resource ) {
|
||||
if ( ! is_object($resource) ) {
|
||||
dbg_error_log( 'PUT', 'update_scheduling_requests called with non-object parameter (%s)', gettype($resource) );
|
||||
return;
|
||||
@ -420,7 +427,7 @@ function import_collection( $ics_content, $user_no, $path, $caldav_context, $app
|
||||
}
|
||||
}
|
||||
|
||||
$calendar = new iCalComponent($ics_content);
|
||||
$calendar = new vComponent($ics_content);
|
||||
$timezones = $calendar->GetComponents('VTIMEZONE',true);
|
||||
$components = $calendar->GetComponents('VTIMEZONE',false);
|
||||
|
||||
@ -436,7 +443,7 @@ function import_collection( $ics_content, $user_no, $path, $caldav_context, $app
|
||||
$tz_ids[$tz->GetPValue('TZID')] = $k;
|
||||
}
|
||||
|
||||
/** Build an array of resources. Each resource is an array of iCalComponent */
|
||||
/** Build an array of resources. Each resource is an array of vComponent */
|
||||
$resources = array();
|
||||
foreach( $components AS $k => $comp ) {
|
||||
$uid = $comp->GetPValue('UID');
|
||||
@ -449,9 +456,8 @@ function import_collection( $ics_content, $user_no, $path, $caldav_context, $app
|
||||
$resources[$uid][] = $comp;
|
||||
|
||||
/** Ensure we have the timezone component for this in our array as well */
|
||||
$tzid = $comp->GetPParamValue('DTSTART', 'TZID');
|
||||
if ( !isset($tzid) || $tzid == '' ) $tzid = $comp->GetPParamValue('DUE','TZID');
|
||||
if ( !isset($resources[$uid][$tzid]) && isset($tz_ids[$tzid]) ) {
|
||||
$tzid = GetTZID($comp);
|
||||
if ( !empty($tzid) && !isset($resources[$uid][$tzid]) && isset($tz_ids[$tzid]) ) {
|
||||
$resources[$uid][$tzid] = $timezones[$tz_ids[$tzid]];
|
||||
}
|
||||
}
|
||||
@ -491,8 +497,7 @@ EOSQL;
|
||||
if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) $qry->Begin();
|
||||
|
||||
/** Construct the VCALENDAR data */
|
||||
$vcal = new iCalComponent();
|
||||
$vcal->VCalendar();
|
||||
$vcal = new vCalendar();
|
||||
$vcal->SetComponents($resource);
|
||||
create_scheduling_requests($vcal);
|
||||
$icalendar = $vcal->Render();
|
||||
@ -549,7 +554,8 @@ EOSQL;
|
||||
* So we're looking for 'VALUE=DATE', to identify the duration, effectively.
|
||||
*
|
||||
*/
|
||||
$value_type = $first->GetPParamValue('DTSTART','VALUE');
|
||||
$dtstart_prop = $first->GetProperty('DTSTART');
|
||||
$value_type = $dtstart_prop->GetParameterValue('VALUE');
|
||||
dbg_error_log('PUT','DTSTART without DTEND. DTSTART value type is %s', $value_type );
|
||||
if ( isset($value_type) && $value_type == 'DATE' )
|
||||
$dtend = '(:dtstart::timestamp with time zone::date + \'1 day\'::interval)';
|
||||
@ -563,7 +569,7 @@ EOSQL;
|
||||
$calitem_params[':modified'] = $last_modified;
|
||||
|
||||
$dtstamp = $first->GetPValue('DTSTAMP');
|
||||
if ( !isset($dtstamp) || $dtstamp == '' ) $dtstamp = $last_modified;
|
||||
if ( empty($dtstamp) ) $dtstamp = $last_modified;
|
||||
$calitem_params[':dtstamp'] = $dtstamp;
|
||||
|
||||
/** RFC2445, 4.8.1.3: Default is PUBLIC, or also if overridden by the collection settings */
|
||||
@ -573,9 +579,8 @@ EOSQL;
|
||||
|
||||
|
||||
/** Calculate what timezone to set, first, if possible */
|
||||
$tzid = $first->GetPParamValue('DTSTART','TZID');
|
||||
if ( !isset($tzid) || $tzid == '' ) $tzid = $first->GetPParamValue('DUE','TZID');
|
||||
if ( isset($tzid) && $tzid != '' ) {
|
||||
$tzid = GetTZID($first);
|
||||
if ( !empty($tzid) ) {
|
||||
if ( isset($resource[$tzid]) ) {
|
||||
$tz = $resource[$tzid];
|
||||
$tz_locn = $tz->GetPValue('X-LIC-LOCATION');
|
||||
@ -649,14 +654,14 @@ EOSQL;
|
||||
|
||||
|
||||
/**
|
||||
* Given a dav_id and an original iCalComponent, pull out each of the VALARMs
|
||||
* Given a dav_id and an original vComponent, pull out each of the VALARMs
|
||||
* and write the values into the calendar_alarm table.
|
||||
*
|
||||
* @param int $dav_id The dav_id of the caldav_data we're processing
|
||||
* @param iCalComponent The VEVENT or VTODO containing the VALARM
|
||||
* @param vComponent The VEVENT or VTODO containing the VALARM
|
||||
* @return null
|
||||
*/
|
||||
function write_alarms( $dav_id, $ical ) {
|
||||
function write_alarms( $dav_id, vComponent $ical ) {
|
||||
$qry = new AwlQuery('DELETE FROM calendar_alarm WHERE dav_id = '.$dav_id );
|
||||
$qry->Exec('PUT',__LINE__,__FILE__);
|
||||
|
||||
@ -710,10 +715,10 @@ function write_alarms( $dav_id, $ical ) {
|
||||
* Parse out the attendee property and write a row to the
|
||||
* calendar_attendee table for each one.
|
||||
* @param int $dav_id The dav_id of the caldav_data we're processing
|
||||
* @param iCalComponent The VEVENT or VTODO containing the ATTENDEEs
|
||||
* @param vComponent The VEVENT or VTODO containing the ATTENDEEs
|
||||
* @return null
|
||||
*/
|
||||
function write_attendees( $dav_id, $ical ) {
|
||||
function write_attendees( $dav_id, vComponent $ical ) {
|
||||
$qry = new AwlQuery('DELETE FROM calendar_attendee WHERE dav_id = '.$dav_id );
|
||||
$qry->Exec('PUT',__LINE__,__FILE__);
|
||||
|
||||
@ -754,7 +759,7 @@ function write_attendees( $dav_id, $ical ) {
|
||||
* @param int $collection_id The ID of the collection containing the resource being written
|
||||
* @param int $author The user_no who wants to put this resource on the server
|
||||
* @param string $etag An etag unique for this event
|
||||
* @param object $ic The parsed iCalendar object
|
||||
* @param vComponent $ic The parsed iCalendar object
|
||||
* @param string $put_action_type INSERT or UPDATE depending on what we are to do
|
||||
* @param boolean $caldav_context True, if we are responding via CalDAV, false for other ways of calling this
|
||||
* @param string Either 'INSERT' or 'UPDATE': the type of action we are doing
|
||||
@ -762,7 +767,8 @@ function write_attendees( $dav_id, $ical ) {
|
||||
* @param string $weak_etag An etag that is NOT modified on ATTENDEE changes for this event
|
||||
* @return boolean True for success, false for failure.
|
||||
*/
|
||||
function write_resource( $user_no, $path, $caldav_data, $collection_id, $author, $etag, $ic, $put_action_type, $caldav_context, $log_action=true, $weak_etag=null ) {
|
||||
function write_resource( $path, $caldav_data, DAVResource $collection, $author, $etag,
|
||||
vComponent $ic, $put_action_type, $caldav_context, $log_action=true, $weak_etag=null ) {
|
||||
global $tz_regex;
|
||||
|
||||
$resources = $ic->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE
|
||||
@ -774,8 +780,15 @@ function write_resource( $user_no, $path, $caldav_data, $collection_id, $author,
|
||||
}
|
||||
else {
|
||||
$first = $resources[0];
|
||||
if ( !($first instanceof vComponent) ) {
|
||||
print $ic->Render();
|
||||
fatal('This is not a vComponent!');
|
||||
}
|
||||
$resource_type = $first->GetType();
|
||||
}
|
||||
|
||||
$user_no = $collection->user_no();
|
||||
$collection_id = $collection->collection_id();
|
||||
|
||||
$qry = new AwlQuery();
|
||||
$qry->Begin();
|
||||
@ -806,13 +819,14 @@ function write_resource( $user_no, $path, $caldav_data, $collection_id, $author,
|
||||
$dav_id = $row->dav_id;
|
||||
$dav_params[':dav_id'] = $dav_id;
|
||||
$calitem_params[':dav_id'] = $dav_id;
|
||||
|
||||
$dtstart = $first->GetPValue('DTSTART');
|
||||
$calitem_params[':dtstart'] = $dtstart;
|
||||
if ( (!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '' ) {
|
||||
$dtstart = $first->GetPValue('DUE');
|
||||
}
|
||||
|
||||
$due = null;
|
||||
if ( $first->GetType() == 'VTODO' ) $due = $first->GetPValue('DUE');
|
||||
$calitem_params[':due'] = $due;
|
||||
$dtstart = $first->GetPValue('DTSTART');
|
||||
if ( empty($dtstart) ) $dtstart = $due;
|
||||
$calitem_params[':dtstart'] = $dtstart;
|
||||
|
||||
$dtend = $first->GetPValue('DTEND');
|
||||
if ( isset($dtend) && $dtend != '' ) {
|
||||
dbg_error_log( 'PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION') );
|
||||
@ -820,6 +834,7 @@ function write_resource( $user_no, $path, $caldav_data, $collection_id, $author,
|
||||
$dtend = ':dtend';
|
||||
}
|
||||
else {
|
||||
// In this case we'll construct the SQL directly as a calculation relative to :dtstart
|
||||
$dtend = 'NULL';
|
||||
if ( $first->GetPValue('DURATION') != '' AND $dtstart != '' ) {
|
||||
$duration = trim(preg_replace( '#[PT]#', ' ', $first->GetPValue('DURATION') ));
|
||||
@ -841,7 +856,8 @@ function write_resource( $user_no, $path, $caldav_data, $collection_id, $author,
|
||||
* So we're looking for 'VALUE=DATE', to identify the duration, effectively.
|
||||
*
|
||||
*/
|
||||
$value_type = $first->GetPParamValue('DTSTART','VALUE');
|
||||
$dtstart_prop = $first->GetProperty('DTSTART');
|
||||
$value_type = $dtstart_prop->GetParameterValue('VALUE');
|
||||
dbg_error_log('PUT','DTSTART without DTEND. DTSTART value type is %s', $value_type );
|
||||
if ( isset($value_type) && $value_type == 'DATE' )
|
||||
$dtend = '(:dtstart::timestamp with time zone::date + \'1 day\'::interval)';
|
||||
@ -882,20 +898,17 @@ function write_resource( $user_no, $path, $caldav_data, $collection_id, $author,
|
||||
}
|
||||
$calitem_params[':class'] = $class;
|
||||
|
||||
|
||||
/** Calculate what timezone to set, first, if possible */
|
||||
$last_tz_locn = 'Turkmenikikamukau'; // I really hope this location doesn't exist!
|
||||
$tzid = $first->GetPParamValue('DTSTART','TZID');
|
||||
if ( !isset($tzid) || $tzid == '' ) $tzid = $first->GetPParamValue('DUE','TZID');
|
||||
$tzid = GetTZID($first);
|
||||
$timezones = $ic->GetComponents('VTIMEZONE');
|
||||
foreach( $timezones AS $k => $tz ) {
|
||||
if ( $tz->GetPValue('TZID') != $tzid ) {
|
||||
/**
|
||||
* We'll pretend they didn't forget to give us a TZID and that they
|
||||
* really hope the server is running in the timezone they supplied... but be noisy about it.
|
||||
* We'll skip any tz definitions that are for a TZID other than the DTSTART/DUE on the first VEVENT/VTODO
|
||||
*/
|
||||
dbg_error_log( 'ERROR', ' Event includes TZID[%s] but uses TZID[%s]!', $tz->GetPValue('TZID'), $tzid );
|
||||
$tzid = $tz->GetPValue('TZID');
|
||||
dbg_error_log( 'ERROR', ' Event uses TZID[%s], skipping included TZID[%s]!', $tz->GetPValue('TZID'), $tzid );
|
||||
continue;
|
||||
}
|
||||
// This is the one
|
||||
$tz_locn = $tz->GetPValue('X-LIC-LOCATION');
|
||||
@ -941,13 +954,12 @@ function write_resource( $user_no, $path, $caldav_data, $collection_id, $author,
|
||||
$calitem_params[':rrule'] = $first->GetPValue('RRULE');
|
||||
$calitem_params[':url'] = $first->GetPValue('URL');
|
||||
$calitem_params[':priority'] = $first->GetPValue('PRIORITY');
|
||||
$calitem_params[':due'] = $first->GetPValue('DUE');
|
||||
$calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
|
||||
$calitem_params[':status'] = $first->GetPValue('STATUS');
|
||||
|
||||
if ( !isset($dav_params[':modified']) ) $dav_params[':modified'] = 'now';
|
||||
if ( $put_action_type == 'INSERT' ) {
|
||||
create_scheduling_requests($vcal);
|
||||
if ( !$collection->IsSchedulingCollection() ) create_scheduling_requests($ic);
|
||||
$sql = 'INSERT INTO caldav_data ( dav_id, user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id, weak_etag )
|
||||
VALUES( :dav_id, :user_no, :dav_name, :etag, :dav_data, :caldav_type, :session_user, :created, :modified, :collection_id, :weak_etag )';
|
||||
$dav_params[':collection_id'] = $collection_id;
|
||||
@ -956,11 +968,12 @@ function write_resource( $user_no, $path, $caldav_data, $collection_id, $author,
|
||||
$dav_params[':created'] = (isset($created) && $created != '' ? $created : $dtstamp);
|
||||
}
|
||||
else {
|
||||
update_scheduling_requests($vcal);
|
||||
if ( !$collection->IsSchedulingCollection() ) update_scheduling_requests($ic);
|
||||
$sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, caldav_type=:caldav_type, logged_user=:session_user,
|
||||
modified=:modified, weak_etag=:weak_etag WHERE dav_id=:dav_id';
|
||||
}
|
||||
if ( !$qry->QDo($sql,$dav_params) ) {
|
||||
fatal('Insert into calendar_item failed...');
|
||||
rollback_on_error( $caldav_context, $user_no, $path);
|
||||
return false;
|
||||
}
|
||||
@ -972,7 +985,7 @@ INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp,
|
||||
dtstart, dtend, summary, location, class, transp,
|
||||
description, rrule, tz_id, last_modified, url, priority,
|
||||
created, due, percent_complete, status, collection_id )
|
||||
VALUES ( :user_no, :dav_name, currval('dav_id_seq'), :etag, :uid, :dtstamp,
|
||||
VALUES ( :user_no, :dav_name, :dav_id, :etag, :uid, :dtstamp,
|
||||
:dtstart, $dtend, :summary, :location, :class, :transp,
|
||||
:description, :rrule, :tzid, :modified, :url, :priority,
|
||||
:created, :due, :percent_complete, :status, :collection_id )
|
||||
@ -986,8 +999,9 @@ EOSQL;
|
||||
else {
|
||||
$sql = <<<EOSQL
|
||||
UPDATE calendar_item SET dav_etag=:etag, uid=:uid, dtstamp=:dtstamp,
|
||||
dtstart=:dtstart, dtend=$dtend, summary=:summary, location=:location, class=:class, transp=:transp,
|
||||
description=:description, rrule=:rrule, tz_id=:tzid, last_modified=:modified, url=:url, priority=:priority,
|
||||
dtstart=:dtstart, dtend=$dtend, summary=:summary, location=:location,
|
||||
class=:class, transp=:transp, description=:description, rrule=:rrule,
|
||||
tz_id=:tzid, last_modified=:modified, url=:url, priority=:priority,
|
||||
due=:due, percent_complete=:percent_complete, status=:status
|
||||
WHERE dav_id=:dav_id
|
||||
EOSQL;
|
||||
@ -1037,18 +1051,16 @@ EOSQL;
|
||||
function simple_write_resource( $path, $caldav_data, $put_action_type, $write_action_log = false ) {
|
||||
|
||||
$etag = md5($caldav_data);
|
||||
$ic = new iCalComponent( $caldav_data );
|
||||
|
||||
/**
|
||||
* We pull the user_no & collection_id out of the collection table, based on the resource path
|
||||
*/
|
||||
$collection_path = preg_replace( '#/[^/]*$#', '/', $path );
|
||||
$qry = new AwlQuery( 'SELECT user_no, collection_id FROM collection WHERE dav_name = :dav_name ', array( ':dav_name' => $collection_path ) );
|
||||
if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 1 ) {
|
||||
$collection = $qry->Fetch();
|
||||
$user_no = $collection->user_no;
|
||||
$collection = new DAVResource($collection_path);
|
||||
if ( $collection->IsCollection() || $collection->IsSchedulingCollection() ) {
|
||||
|
||||
return write_resource( $user_no, $path, $caldav_data, $collection->collection_id, $user_no, $etag, $ic, $put_action_type, false, $write_action_log );
|
||||
$vc = new vComponent( $caldav_data );
|
||||
return write_resource( $path, $caldav_data, $collection, $user_no, $etag, $vc, $put_action_type, false, $write_action_log );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ if ( $dav_resource->IsCollection() ) {
|
||||
}
|
||||
|
||||
$etag = md5($request->raw_post);
|
||||
$ic = new iCalComponent( $request->raw_post );
|
||||
$ic = new vComponent( $request->raw_post );
|
||||
|
||||
if ( ! $dav_resource->Exists() && (isset($request->etag_if_match) && $request->etag_if_match != '') ) {
|
||||
/**
|
||||
@ -90,8 +90,9 @@ if ( $dav_resource->Exists() ) {
|
||||
}
|
||||
|
||||
$put_action_type = ($dav_resource->Exists() ? 'UPDATE' : 'INSERT');
|
||||
$collection = $dav_resource->GetParentContainer();
|
||||
|
||||
write_resource( $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), $request->raw_post, $dav_resource->GetProperty('collection_id'),
|
||||
write_resource( $dav_resource->bound_from(), $request->raw_post, $collection,
|
||||
$session->user_no, $etag, $ic, $put_action_type, true, true );
|
||||
|
||||
header(sprintf('ETag: "%s"', $etag) );
|
||||
|
||||
71
testing/tests/scheduling/3010-PUT-with-attendees.result
Normal file
71
testing/tests/scheduling/3010-PUT-with-attendees.result
Normal file
@ -0,0 +1,71 @@
|
||||
HTTP/1.1 100 Continue
|
||||
|
||||
HTTP/1.1 204 No Content
|
||||
Date: Dow, 01 Jan 2000 00:00:00 GMT
|
||||
DAV: 1, 2, 3, access-control, calendar-access, calendar-schedule
|
||||
DAV: extended-mkcol, calendar-proxy, bind, addressbook, calendar-auto-schedule
|
||||
ETag: "e69b6d5b70189b96126c8993bcf1648a"
|
||||
Content-Length: 0
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
|
||||
|
||||
caldav_type: >VEVENT<
|
||||
dav_etag: >e69b6d5b70189b96126c8993bcf1648a<
|
||||
dtend: >2010-11-23 16:00:00<
|
||||
dtstamp: >2010-11-24 22:19:00<
|
||||
dtstart: >2010-11-23 11:00:00<
|
||||
due: >NULL<
|
||||
location: >NULL<
|
||||
logged_user: >10<
|
||||
summary: >Multiple Beard Meeting<
|
||||
uid: >PUT-attendees<
|
||||
user_no: >10<
|
||||
|
||||
caldav_type: >VEVENT<
|
||||
dav_etag: >e69b6d5b70189b96126c8993bcf1648a<
|
||||
dtend: >2010-11-23 16:00:00<
|
||||
dtstamp: >2010-11-24 22:19:00<
|
||||
dtstart: >2010-11-23 11:00:00<
|
||||
due: >NULL<
|
||||
location: >NULL<
|
||||
logged_user: >10<
|
||||
summary: >Multiple Beard Meeting<
|
||||
uid: >PUT-attendees<
|
||||
user_no: >11<
|
||||
|
||||
caldav_type: >VEVENT<
|
||||
dav_etag: >e69b6d5b70189b96126c8993bcf1648a<
|
||||
dtend: >2010-11-23 16:00:00<
|
||||
dtstamp: >2010-11-24 22:19:00<
|
||||
dtstart: >2010-11-23 11:00:00<
|
||||
due: >NULL<
|
||||
location: >NULL<
|
||||
logged_user: >10<
|
||||
summary: >Multiple Beard Meeting<
|
||||
uid: >PUT-attendees<
|
||||
user_no: >12<
|
||||
|
||||
caldav_type: >VEVENT<
|
||||
dav_etag: >e69b6d5b70189b96126c8993bcf1648a<
|
||||
dtend: >2010-11-23 16:00:00<
|
||||
dtstamp: >2010-11-24 22:19:00<
|
||||
dtstart: >2010-11-23 11:00:00<
|
||||
due: >NULL<
|
||||
location: >NULL<
|
||||
logged_user: >10<
|
||||
summary: >Multiple Beard Meeting<
|
||||
uid: >PUT-attendees<
|
||||
user_no: >13<
|
||||
|
||||
caldav_type: >VEVENT<
|
||||
dav_etag: >e69b6d5b70189b96126c8993bcf1648a<
|
||||
dtend: >2010-11-23 16:00:00<
|
||||
dtstamp: >2010-11-24 22:19:00<
|
||||
dtstart: >2010-11-23 11:00:00<
|
||||
due: >NULL<
|
||||
location: >NULL<
|
||||
logged_user: >10<
|
||||
summary: >Multiple Beard Meeting<
|
||||
uid: >PUT-attendees<
|
||||
user_no: >101<
|
||||
|
||||
74
testing/tests/scheduling/3010-PUT-with-attendees.test
Normal file
74
testing/tests/scheduling/3010-PUT-with-attendees.test
Normal file
@ -0,0 +1,74 @@
|
||||
#
|
||||
# PUT an event with attendees
|
||||
#
|
||||
TYPE=PUT
|
||||
URL=http://mycaldav/caldav.php/resource2/home/PUT-attendees.ics
|
||||
HEADER=Content-Type: text/calendar
|
||||
HEADER=User-Agent: DAVKit/2.0 (10.5.1; wrbt) iCal 3.0.1
|
||||
HEAD
|
||||
|
||||
|
||||
BEGINDATA
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Apple Inc.//iCal 3.0//EN
|
||||
CALSCALE:GREGORIAN
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Pacific/Auckland
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+1300
|
||||
TZOFFSETTO:+1200
|
||||
DTSTART:19900318T030000
|
||||
RRULE:FREQ=YEARLY;UNTIL=20070317T140000Z;BYMONTH=3;BYDAY=3SU
|
||||
TZNAME:NZST
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+1200
|
||||
TZOFFSETTO:+1300
|
||||
DTSTART:20070930T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU
|
||||
TZNAME:NZDT
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+1300
|
||||
TZOFFSETTO:+1200
|
||||
DTSTART:20080406T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU
|
||||
TZNAME:NZST
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
SEQUENCE:7
|
||||
DESCRIPTION:Masses of Lunch will be needed
|
||||
UID:PUT-attendees
|
||||
TRANSP:OPAQUE
|
||||
DTSTART;TZID=Pacific/Auckland:20101123T110000
|
||||
DTEND;TZID=Pacific/Auckland:20101123T160000
|
||||
DTSTAMP:20101124T221900Z
|
||||
SUMMARY:Multiple Beard Meeting
|
||||
CREATED:20071124T221521Z
|
||||
RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=4TU
|
||||
ORGANIZER;CN=User 1:MAILTO:user1@example.net
|
||||
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;
|
||||
RSVP=TRUE;CN=User 1;LANGUAGE=en:MAILTO:user1@example.net
|
||||
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;
|
||||
RSVP=TRUE;CN=User 2;LANGUAGE=en:MAILTO:user2@example.net
|
||||
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;
|
||||
RSVP=TRUE;CN=User 3;LANGUAGE=en:MAILTO:user3@example.net
|
||||
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;
|
||||
RSVP=TRUE;CN=User 4;LANGUAGE=en:MAILTO:user4@example.net
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
ENDDATA
|
||||
|
||||
|
||||
QUERY
|
||||
SELECT caldav_data.user_no, caldav_data.dav_etag, caldav_type, logged_user,
|
||||
uid, dtstamp,
|
||||
dtstart at time zone tz_locn as dtstart,
|
||||
dtend at time zone tz_locn as dtend,
|
||||
due, summary, location
|
||||
FROM caldav_data JOIN calendar_item USING(dav_name) LEFT JOIN time_zone USING (tz_id)
|
||||
WHERE calendar_item.uid = 'PUT-attendees';
|
||||
ENDQUERY
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user