* Calendar/REST API: adding of participants to events

This commit is contained in:
ralf 2023-09-18 14:13:09 +02:00
parent 19552059b3
commit b6a0e650fe
3 changed files with 110 additions and 14 deletions

View File

@ -95,7 +95,7 @@ class JsCalendar
* @param string $method='PUT' 'PUT', 'POST' or 'PATCH' * @param string $method='PUT' 'PUT', 'POST' or 'PATCH'
* @return array * @return array
*/ */
public static function parseJsEvent(string $json, array $old=[], string $content_type=null, $method='PUT') public static function parseJsEvent(string $json, array $old=[], string $content_type=null, $method='PUT', int $calendar_owner=null)
{ {
try try
{ {
@ -147,7 +147,7 @@ class JsCalendar
break; break;
case 'participants': case 'participants':
$event['participants'] = self::parseParticipants($value); $event['participants'] = self::parseParticipants($value, $strict, $calendar_owner);
break; break;
case 'priority': case 'priority':
@ -537,21 +537,22 @@ class JsCalendar
const TYPE_PARTICIPANT = 'Participant'; const TYPE_PARTICIPANT = 'Participant';
static $status2jscal = [
'U' => 'needs-action',
'A' => 'accepted',
'R' => 'declined',
'T' => 'tentative',
//'' => 'delegated',
];
/** /**
* Return participants object * Return participants object
* *
* @param array $event * @param array $event
* @return array * @return array
*/ * @todo Resources and Groups without email */
protected static function Participants(array $event) protected static function Participants(array $event)
{ {
static $status2jscal = [
'U' => 'needs-action',
'A' => 'accepted',
'R' => 'declined',
'T' => 'tentative',
//'' => 'delegated',
];
$participants = []; $participants = [];
foreach($event['participants'] as $uid => $status) foreach($event['participants'] as $uid => $status)
{ {
@ -589,7 +590,7 @@ class JsCalendar
'optional' => $role === 'OPT-PARTICIPANT', 'optional' => $role === 'OPT-PARTICIPANT',
'informational' => $role === 'NON-PARTICIPANT', 'informational' => $role === 'NON-PARTICIPANT',
]), ]),
'participationStatus' => $status2jscal[$status], 'participationStatus' => self::$status2jscal[$status],
]); ]);
$participants[$uid] = $participant; $participants[$uid] = $participant;
} }
@ -597,6 +598,101 @@ class JsCalendar
return $participants; return $participants;
} }
/**
* Parse participants object
*
* @param array $participants
* @param bool $strict true: require @types and objects with attributes name, email, ...
* @param ?int $calendar_owner owner of the calendar / collection
* @return array
* @todo Resources and Groups without email
*/
protected static function parseParticipants(array $participants, bool $strict=true, int $calendar_owner=null)
{
$parsed = [];
foreach($participants as $uid => $participant)
{
if ($strict && (!is_array($participant) || $participant[self::AT_TYPE] !== self::TYPE_PARTICIPANT))
{
throw new \InvalidArgumentException("Missing or invalid @type: ".json_encode($participant, self::JSON_OPTIONS_ERROR));
}
elseif (!is_array($participant))
{
$participant = [
'email' => $participant,
];
}
// check if the uid is valid and matches the data in the object
if (($test_uid = self::Participants(['participants' => [
$uid => 'U'
]])) && ($test_uid['email'] ?? null) === $participant['email'] &&
($test_uid['kind'] ?? null) === ($participant['kind'] ?? null) &&
($test_uid['name'] ?? null) === ($participant['name'] ?? null))
{
// use $uid as is
}
else
{
if (empty($participant['email']) || !preg_match(Api\Etemplate\Widget\Url::EMAIL_PREG, $participant['email']))
{
throw new \InvalidArgumentException("Missing or invalid email address: ".json_encode($participant, self::JSON_OPTIONS_ERROR));
}
static $contacts = null;
if (!isset($contacts)) $contacts = new Api\Contacts();
if ((list($data) = $contacts->search([
'email' => $participant['email'],
'email_home' => $participant['email'],
], ['id','egw_addressbook.account_id as account_id','n_fn'],
'egw_addressbook.account_id IS NOT NULL DESC, n_fn IS NOT NULL DESC',
'','',false,'OR')))
{
// found an addressbook entry
$uid = $data['account_id'] ? (int)$data['account_id'] : 'c'.$data['id'];
}
else
{
$uid = 'e'.(empty($participant['name']) ? $participant['email'] : $participant['name'].' <'.$participant['email'].'>');
}
}
$default_status = $uid === $GLOBALS['egw_info']['user']['account_id'] ? 'A' : 'U';
$default_role = $uid === $calendar_owner ? 'CHAIR' : 'REQ-PARTICIPANT';
$parsed[$uid] = \calendar_so::combine_status(array_search($participant['participationStatus'] ?? $default_status, self::$status2jscal) ?: $default_status,
1, self::jscalRoles2role($participant['roles'] ?? null, $default_role));
}
return $parsed;
}
protected static function jscalRoles2role(array $roles=null, string $default_role=null)
{
$role = $default_role ?? 'REQ-PARTICIPANT';
foreach($roles ?? [] as $name => $value)
{
if ($value && $role !== 'CHAIR')
{
switch($name)
{
case 'owner': // we ignore the owner, it's set automatic to the owner of the calendar/collection
break;
case 'attendee':
$role = 'REQ-PARTICIPANT';
break;
case 'optional':
$role = 'OPT-PARTICIPANT';
break;
case 'informational':
$role = 'NON-PARTICIPANT';
break;
case 'chair':
$role = 'CHAIR';
break;
}
}
}
return $role;
}
const TYPE_LOCATION = 'Location'; const TYPE_LOCATION = 'Location';
const TYPE_VIRTALLOCATION = 'VirtualLocation'; const TYPE_VIRTALLOCATION = 'VirtualLocation';

View File

@ -1227,7 +1227,7 @@ class calendar_groupdav extends Api\CalDAV\Handler
$type = null; $type = null;
if (($is_json=Api\CalDAV::isJSON($type))) if (($is_json=Api\CalDAV::isJSON($type)))
{ {
$event = Api\CalDAV\JsCalendar::parseJsEvent($options['content'], $oldEvent ?? [], $type, $method); $event = Api\CalDAV\JsCalendar::parseJsEvent($options['content'], $oldEvent ?? [], $type, $method, $user);
$cal_id = $this->bo->save($event); $cal_id = $this->bo->save($event);
} }
else else

View File

@ -202,7 +202,7 @@ curl https://example.org/egroupware/groupdav.php/<username>/calendar/ -H "Accept
following GET parameters are supported to customize the returned properties: following GET parameters are supported to customize the returned properties:
- props[]=<DAV-prop-name> eg. props[]=getetag to return only the ETAG (multiple DAV properties can be specified) - props[]=<DAV-prop-name> eg. props[]=getetag to return only the ETAG (multiple DAV properties can be specified)
Default for addressbook collections is to only return address-data (JsContact), other collections return all props. Default for calendar collections is to only return calendar-data (JsEvent), other collections return all props.
- sync-token=<token> to only request change since last sync-token, like rfc6578 sync-collection REPORT - sync-token=<token> to only request change since last sync-token, like rfc6578 sync-collection REPORT
- nresults=N limit number of responses (only for sync-collection / given sync-token parameter!) - nresults=N limit number of responses (only for sync-collection / given sync-token parameter!)
this will return a "more-results"=true attribute and a new "sync-token" attribute to query for the next chunk this will return a "more-results"=true attribute and a new "sync-token" attribute to query for the next chunk
@ -215,7 +215,7 @@ Examples: see addressbook
<summary>Example: GET request for a single resource</summary> <summary>Example: GET request for a single resource</summary>
``` ```
curl 'https://example.org/egroupware/groupdav.php/addressbook/6502' -H "Accept: application/pretty+json" --user <username> curl 'https://example.org/egroupware/groupdav.php/calendar/6502' -H "Accept: application/pretty+json" --user <username>
{ {
"@type": "Event", "@type": "Event",
"prodId": "EGroupware Calendar 23.1.002", "prodId": "EGroupware Calendar 23.1.002",