From 4f01fe0a17ac90005c659381e89f157f80c7ad3a Mon Sep 17 00:00:00 2001 From: Andrew McMillan Date: Sat, 10 Sep 2011 00:17:27 +1200 Subject: [PATCH] Add items to attendee's calendars as well as to their inboxes on PUT. --- inc/WritableCollection.php | 54 +++++--- inc/caldav-PUT-functions.php | 126 +++++++----------- testing/tests/binding/1032-BIND-admin.result | 2 +- testing/tests/binding/1035-GET-mashup.result | 27 +++- .../1036-REPORT-sync-initial-bound.result | 13 +- testing/tests/binding/1100-BIND.result | 2 +- .../tests/binding/1101-PROPFIND-normal.result | 2 +- .../tests/binding/1102-PROPFIND-bound.result | 2 +- testing/tests/carddav/Restore-Database.result | 2 +- .../scheduling/3010-PUT-with-attendees.result | 4 +- .../tests/scheduling/Restore-Database.result | 2 +- 11 files changed, 131 insertions(+), 105 deletions(-) diff --git a/inc/WritableCollection.php b/inc/WritableCollection.php index 93e38913..e9545057 100644 --- a/inc/WritableCollection.php +++ b/inc/WritableCollection.php @@ -4,12 +4,19 @@ include_once('DAVResource.php'); 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 vComponent $data The resource to be written. + * Writes the data to a member in the collection and returns the segment_name of the + * resource in our internal namespace. + * + * @param vCalendar $vcal 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. + * @param boolean $do_scheduling True if we should also do scheduling for this write. Default false. + * @param string $segment_name The name of the resource within the collection, or null if this + * call should invent one based on the UID of the vCalendar. + * @param boolean $log_action Whether to log this action. Defaults to false since this is normally called + * in situations where one is writing secondary data. + * @return string The segment_name of the resource within the collection, as written, or false on failure. */ - function WriteCalendarMember( vComponent $data, $create_resource, $do_scheduling=true, $segment_name = null ) { + function WriteCalendarMember( vCalendar $vcal, $create_resource, $do_scheduling=false, $segment_name = null, $log_action=false ) { if ( !$this->IsSchedulingCollection() && !$this->IsCalendar() ) { dbg_error_log( 'PUT', '"%s" is not a calendar or scheduling collection!', $this->dav_name); return false; @@ -17,7 +24,7 @@ class WritableCollection extends DAVResource { global $tz_regex, $session, $caldav_context; - $resources = $data->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE + $resources = $vcal->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE $user_no = $this->user_no(); $collection_id = $this->collection_id(); @@ -36,7 +43,7 @@ class WritableCollection extends DAVResource { } $path = $this->dav_name() . $segment_name; - $caldav_data = $data->Render(); + $caldav_data = $vcal->Render(); $etag = md5($caldav_data); $weak_etag = null; @@ -53,6 +60,7 @@ class WritableCollection extends DAVResource { } if ( $qry->rows() != 1 || !($row = $qry->Fetch()) ) { // No dav_id? => We're toast! + dbg_error_log( 'PUT', 'No dav_id!!!', $path); rollback_on_error( $caldav_context, $user_no, $path); return false; } @@ -73,13 +81,13 @@ class WritableCollection extends DAVResource { ) ); if ( $create_resource ) { - if ( !$this->IsSchedulingCollection() ) create_scheduling_requests($vcal); + if ( !$this->IsSchedulingCollection() && $do_scheduling ) do_scheduling_requests($vcal,true); $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); + if ( !$this->IsSchedulingCollection() && $do_scheduling ) do_scheduling_requests($vcal,false); $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'; } @@ -168,7 +176,7 @@ class WritableCollection extends DAVResource { $due_prop = $first->GetProperty('DUE'); $tzid = $due_prop->GetParameterValue('TZID'); } - $timezones = $data->GetComponents('VTIMEZONE'); + $timezones = $vcal->GetComponents('VTIMEZONE'); foreach( $timezones AS $k => $tz ) { if ( $tz->GetPValue('TZID') != $tzid ) { /** @@ -253,7 +261,7 @@ EOSQL; if ( !$this->IsSchedulingCollection() ) { write_alarms($dav_id, $first); - write_attendees($dav_id, $first); + write_attendees($dav_id, $vcal); if ( $log_action && function_exists('log_caldav_action') ) { log_caldav_action( $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path ); } @@ -278,25 +286,35 @@ 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. + * Writes the data to a member in the collection and returns the segment_name of the + * resource in our internal namespace. + * + * A caller who wants scheduling not to happen for this write must already + * know they are dealing with a calendar, so should be calling WriteCalendarMember + * directly. + * + * @param $resource 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, or false on failure. + * @param boolean $log_action Whether to log this action. Defaults to true since this is normally called + * in situations where one is writing primary data. + * @return string The segment_name that was given, or one that was assigned if null was given. */ - function WriteMember( $data, $create_resource, $segment_name = null ) { + function WriteMember( $resource, $create_resource, $segment_name = null, $log_action=true ) { if ( ! $this->IsCollection() ) { dbg_error_log( 'PUT', '"%s" is not a collection path', $this->dav_name); return false; } - if ( ! is_object($data) ) { + if ( ! is_object($resource) ) { 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); + if ( $resource instanceof vCalendar ) { + return $this->WriteCalendarMember($resource,$create_resource,true,$segment_name,$log_action); + } + else if ( $resource instanceof VCard ) + return $this->WriteAddressbookMember($resource,$create_resource,$segment_name, $log_action); return $segment_name; } diff --git a/inc/caldav-PUT-functions.php b/inc/caldav-PUT-functions.php index b004c9cb..292e5b1e 100644 --- a/inc/caldav-PUT-functions.php +++ b/inc/caldav-PUT-functions.php @@ -309,83 +309,59 @@ function handle_schedule_reply ( vCalendar $ical ) { } - - /** -* Create a scheduling request in the schedule inbox for the +* Create/Update the scheduling requests for this resource. This includes updating +* the scheduled user's default calendar. * @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 +* @param boolean $create true if the scheduling requests are being created. */ -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) === false ) { - $response = '5.3;'.translate('No scheduling support for user'); +function do_scheduling_requests( vCalendar $resource, $create ) { + if ( ! is_object($resource) ) { + dbg_error_log( 'PUT', 'do_scheduling_requests called with non-object parameter (%s)', gettype($resource) ); + return; + } + + $attendees = $resource->GetAttendees(); + if ( count($attendees) == 0 ) { + dbg_error_log( 'PUT', 'Event has no attendees - no scheduling required.', count($attendees) ); + return; + } + + dbg_error_log( 'PUT', 'Adding to scheduling inbox %d attendees', count($attendees) ); + $schedule_request = clone($resource); + $schedule_request->AddProperty('METHOD','REQUEST'); + foreach( $attendees AS $attendee ) { + $email = preg_replace( '/^mailto:/i', '', $attendee->Value() ); + $schedule_target = new Principal('email',$email); + if ( $schedule_target->Exists() ) { + $attendee_calendar = new WritableCollection(array('path' => $schedule_target->internal_url('schedule-default-calendar'))); + if ( !$attendee_calendar->Exists() ) { + dbg_error_log('ERROR','Default calendar at "%s" does not exist for user "%s"', + $attendee_calendar->dav_name(), $schedule_target->username()); + $response = '5.3;'.translate('No scheduling support for user'); + } + else if ( $attendee_calendar->WriteCalendarMember($resource, $create) === false ) { + dbg_error_log('ERROR','Could not write new calendar member to %s', $attendee_calendar->dav_name(), + $attendee_calendar->dav_name(), $schedule_target->username()); + $response = '5.3;'.translate('No scheduling support for user'); + } + else { + $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($schedule_request, $create) === false ) { + $response = '5.3;'.translate('No scheduling support for user'); + } + else { + $response = '2.0;'.translate('Scheduling invitation delivered successfully'); + } + } } else { - $response = '2.0;'.translate('Scheduling invitation delivered successfully'); + $response = '5.3;'.translate('No scheduling support for user'); } - } - else { - $response = '5.3;'.translate('No scheduling support for user'); - } - return '"'.$response.'"'; -} - -/** -* Create scheduling requests in the schedule inbox for the -* @param vComponent $resource The VEVENT/VTODO/... resource we are scheduling -*/ -function create_scheduling_requests( vCalendar $resource ) { - if ( ! is_object($resource) ) { - dbg_error_log( 'PUT', 'create_scheduling_requests called with non-object parameter (%s)', gettype($resource) ); - return; - } - - $attendees = $resource->GetAttendees(); - if ( count($attendees) == 0 ) { - dbg_error_log( 'PUT', 'Event has no attendees - no scheduling required.', count($attendees) ); - return; - } - - dbg_error_log( 'PUT', 'Adding to scheduling inbox %d attendees', count($attendees) ); - $schedule_request = new VCalendar( array('METHOD' => 'REQUEST') ); - $schedule_request->SetComponents( $resource->GetComponents() ); - foreach( $attendees AS $attendee ) { - $schedule_status = write_scheduling_request( $schedule_request, $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 vComponent $resource The VEVENT/VTODO/... resource we are scheduling -*/ -function update_scheduling_requests( vCalendar $resource ) { - if ( ! is_object($resource) ) { - dbg_error_log( 'PUT', 'update_scheduling_requests called with non-object parameter (%s)', gettype($resource) ); - return; - } - - $attendees = $resource->GetAttendees(); - if ( count($attendees) == 0 ) { - dbg_error_log( 'PUT', 'Event has no attendees - no scheduling required.', count($attendees) ); - return; - } - - dbg_error_log( 'PUT', 'Adding to scheduling inbox %d attendees', count($attendees) ); - $schedule_request = new VCalendar( array('METHOD' => 'REQUEST') ); - $schedule_request->SetComponents( $resource->GetComponents() ); - foreach( $attendees AS $attendee ) { - $schedule_status = write_scheduling_request( $schedule_request, $attendee->Value(), false ); + $schedule_status = '"'.$response.'"'; dbg_error_log( 'PUT', 'Status for attendee <%s> set to "%s"', $attendee->Value(), $schedule_status ); $attendee->SetParameterValue( 'SCHEDULE-STATUS', $schedule_status ); } @@ -484,7 +460,7 @@ EOSQL; /** Construct the VCALENDAR data */ $vcal = new vCalendar(); $vcal->SetComponents($resource); - create_scheduling_requests($vcal); + do_scheduling_requests($vcal,true); $icalendar = $vcal->Render(); /** As ever, we mostly deal with the first resource component */ @@ -623,7 +599,7 @@ EOSQL; write_alarms($dav_id, $first); write_attendees($dav_id, $vcal); - create_scheduling_requests( $vcal ); + do_scheduling_requests( $vcal, true ); if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) $qry->Commit(); } @@ -945,7 +921,7 @@ function write_resource( DAVResource $resource, $caldav_data, DAVResource $colle if ( !isset($dav_params[':modified']) ) $dav_params[':modified'] = 'now'; if ( $put_action_type == 'INSERT' ) { - if ( !$collection->IsSchedulingCollection() ) create_scheduling_requests($ic); + if ( !$collection->IsSchedulingCollection() ) do_scheduling_requests($ic,true); $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; @@ -954,7 +930,7 @@ function write_resource( DAVResource $resource, $caldav_data, DAVResource $colle $dav_params[':created'] = (isset($created) && $created != '' ? $created : $dtstamp); } else { - if ( !$collection->IsSchedulingCollection() ) update_scheduling_requests($ic); + if ( !$collection->IsSchedulingCollection() ) do_scheduling_requests($ic,false); $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'; } diff --git a/testing/tests/binding/1032-BIND-admin.result b/testing/tests/binding/1032-BIND-admin.result index d41421be..cec700af 100644 --- a/testing/tests/binding/1032-BIND-admin.result +++ b/testing/tests/binding/1032-BIND-admin.result @@ -55,7 +55,7 @@ target_collection: >11< target_resource_i: >NULL< ticket_owner: >1003< - bind_id: >1624< + bind_id: >1628< bind_owner: >1< bound_source_id: >12< dav_displayname: >user3 home< diff --git a/testing/tests/binding/1035-GET-mashup.result b/testing/tests/binding/1035-GET-mashup.result index 53bec384..708d7b7b 100644 --- a/testing/tests/binding/1035-GET-mashup.result +++ b/testing/tests/binding/1035-GET-mashup.result @@ -2,7 +2,7 @@ HTTP/1.1 200 OK 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 -Content-Length: 20351 +Content-Length: 21247 Etag: "ae93907cb03bc025b8e733eb61f3a09e" Content-Type: text/calendar; charset="utf-8" @@ -380,6 +380,31 @@ X-MOZ-SEND-INVITATIONS:TRUE SEQUENCE:1 END:VEVENT BEGIN:VEVENT +CREATED:20100319T103247Z +LAST-MODIFIED:20100319T105620Z +DTSTAMP:20100319T105620Z +UID:70399cd7-50a4-4be4-a665-af593e19a7fd +SUMMARY:An invited event. Black tie with pink polka dots is essential. + Do not bring a leopard. Any leopards which do attend will be forcibly + chained to a nearby fence. +ORGANIZER;RSVP=TRUE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:user1@example.ne + t +ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT:mailto:use + r2@example.net +ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT:mailto:use + r3@example.net +ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT:mailto:use + r4@example.net +ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT:mailto:use + r5@example.net +DTSTART:20100322T160000 +DTEND:20100322T170000 +X-MOZ-GENERATION:3 +TRANSP:OPAQUE +X-MOZ-SEND-INVITATIONS:TRUE +SEQUENCE:1 +END:VEVENT +BEGIN:VEVENT LAST-MODIFIED:20100125T214235Z SEQUENCE:1 UID:73d1f980-ec28-012c-11f9-002421a2359e diff --git a/testing/tests/binding/1036-REPORT-sync-initial-bound.result b/testing/tests/binding/1036-REPORT-sync-initial-bound.result index c3150356..ac97945c 100644 --- a/testing/tests/binding/1036-REPORT-sync-initial-bound.result +++ b/testing/tests/binding/1036-REPORT-sync-initial-bound.result @@ -2,8 +2,8 @@ HTTP/1.1 207 Multi-Status 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: "669141e7081e1e14a6bef5b2b6327659" -Content-Length: 2040 +ETag: "0d70494d992ba29a4785b464a6325784" +Content-Length: 2286 Content-Type: text/xml; charset="utf-8" @@ -80,5 +80,14 @@ Content-Type: text/xml; charset="utf-8" HTTP/1.1 200 OK + + /caldav.php/user2/home/70399cd7-50a4-4be4-a665-af593e19a7fd.ics + + + "baad41c9f24bd1ecbdde6e58882df9b7" + + HTTP/1.1 200 OK + + 5 diff --git a/testing/tests/binding/1100-BIND.result b/testing/tests/binding/1100-BIND.result index 821d14fd..c91139c8 100644 --- a/testing/tests/binding/1100-BIND.result +++ b/testing/tests/binding/1100-BIND.result @@ -35,7 +35,7 @@ Content-Type: text/plain; charset="utf-8" length: >8< parent_container: >/user4/base/< - bind_id: >1625< + bind_id: >1629< bound_source_id: >1601< dav_displayname: >A normal collection< dav_name: >/user4/boundbase/< diff --git a/testing/tests/binding/1101-PROPFIND-normal.result b/testing/tests/binding/1101-PROPFIND-normal.result index df5e0191..f3609ca3 100644 --- a/testing/tests/binding/1101-PROPFIND-normal.result +++ b/testing/tests/binding/1101-PROPFIND-normal.result @@ -112,7 +112,7 @@ newcalendar - /caldav.php/.resources/1614 + /caldav.php/.resources/1618 diff --git a/testing/tests/binding/1102-PROPFIND-bound.result b/testing/tests/binding/1102-PROPFIND-bound.result index b45ee58b..8c2270a4 100644 --- a/testing/tests/binding/1102-PROPFIND-bound.result +++ b/testing/tests/binding/1102-PROPFIND-bound.result @@ -125,7 +125,7 @@ newcalendar - /caldav.php/.resources/1614 + /caldav.php/.resources/1618 diff --git a/testing/tests/carddav/Restore-Database.result b/testing/tests/carddav/Restore-Database.result index 0781c440..80453e61 100644 --- a/testing/tests/carddav/Restore-Database.result +++ b/testing/tests/carddav/Restore-Database.result @@ -1,6 +1,6 @@ setval -------- - 1625 + 1629 (1 row) setval diff --git a/testing/tests/scheduling/3010-PUT-with-attendees.result b/testing/tests/scheduling/3010-PUT-with-attendees.result index b09f132a..0a51006b 100644 --- a/testing/tests/scheduling/3010-PUT-with-attendees.result +++ b/testing/tests/scheduling/3010-PUT-with-attendees.result @@ -5,12 +5,10 @@ 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: 112 +Content-Length: 0 Content-Type: text/plain; charset="utf-8" -Notice: Undefined variable: log_action in /home/karora/projects/davical/inc/WritableCollection.php on line 259 - caldav_data: >BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Apple Inc.//iCal 3.0//EN diff --git a/testing/tests/scheduling/Restore-Database.result b/testing/tests/scheduling/Restore-Database.result index 4b4188ff..e6084764 100644 --- a/testing/tests/scheduling/Restore-Database.result +++ b/testing/tests/scheduling/Restore-Database.result @@ -1,6 +1,6 @@ setval -------- - 1637 + 1641 (1 row) setval