From c54c127d05e1b36470e058171526b855af8930d8 Mon Sep 17 00:00:00 2001 From: ralf Date: Mon, 6 May 2024 17:32:47 +0200 Subject: [PATCH] * InfoLog: now fully supported by REST API --- api/src/CalDAV/JsBase.php | 4 +- api/src/CalDAV/JsCalendar.php | 15 +- doc/REST-CalDAV-CardDAV/Addressbook.md | 1 + doc/REST-CalDAV-CardDAV/Calendar.md | 1 + doc/REST-CalDAV-CardDAV/Infolog.md | 329 +++++++++++++++++++++ doc/REST-CalDAV-CardDAV/README.md | 5 +- infolog/inc/class.infolog_groupdav.inc.php | 2 +- 7 files changed, 342 insertions(+), 15 deletions(-) create mode 100644 doc/REST-CalDAV-CardDAV/Infolog.md diff --git a/api/src/CalDAV/JsBase.php b/api/src/CalDAV/JsBase.php index d3b5964124..5db49c2445 100644 --- a/api/src/CalDAV/JsBase.php +++ b/api/src/CalDAV/JsBase.php @@ -156,9 +156,9 @@ class JsBase $fields = []; foreach(Api\Storage\Customfields::get($app ?? static::APP) as $name => $data) { - $value = $contact['#'.$name]; - if (isset($value)) + if (isset($contact['#'.$name])) { + $value = $contact['#'.$name]; switch($data['type']) { case 'date-time': diff --git a/api/src/CalDAV/JsCalendar.php b/api/src/CalDAV/JsCalendar.php index 0c28c3ce47..506cfc10a8 100644 --- a/api/src/CalDAV/JsCalendar.php +++ b/api/src/CalDAV/JsCalendar.php @@ -324,11 +324,11 @@ class JsCalendar extends JsBase break; case 'start': - $event['info_startdate'] = self::parseDateTime($value, $data['timeZone'] ?? null, $data['showWithoutTime'] ?? null); + $event['info_startdate'] = self::parseDateTime($value, $data['timeZone'] ?? null, $data['showWithoutTime'] ?? false); break; case 'due': - $event['info_enddate'] = self::parseDateTime($value, $data['timeZone'] ?? null, $data['showWithoutTime'] ?? null); + $event['info_enddate'] = self::parseDateTime($value, $data['timeZone'] ?? null, $data['showWithoutTime'] ?? false); break; case 'egroupware.org:completed': @@ -393,13 +393,7 @@ class JsCalendar extends JsBase } } catch (\Throwable $e) { - self::handleExceptions($e, 'JsCalendar Event', $name, $value); - } - - // if no participant given add current user as CHAIR to the event - if (empty($event['participants'])) - { - $event['participants'][$calendar_owner ?? $GLOBALS['egw_info']['user']['account_id']] = 'ACHAIR'; + self::handleExceptions($e, 'JsTask', $name, $value); } return $event; @@ -784,8 +778,9 @@ class JsCalendar extends JsBase if (is_numeric($uid)) { $info = [ - 'name' => Api\Accounts::id2name($uid, 'account_fullname'), + 'name' => Api\Accounts::id2name($uid, $uid < 0 ? 'account_lid' : 'account_fullname'), 'email' => Api\Accounts::id2name($uid, 'account_email'), + 'kind' => $uid < 0 ? 'group' : 'individual', ]; } else diff --git a/doc/REST-CalDAV-CardDAV/Addressbook.md b/doc/REST-CalDAV-CardDAV/Addressbook.md index 5133a66095..d5b865216d 100644 --- a/doc/REST-CalDAV-CardDAV/Addressbook.md +++ b/doc/REST-CalDAV-CardDAV/Addressbook.md @@ -10,6 +10,7 @@ Following RFCs / drafts used/planned for JSON encoding of resources ([* see at end of document](#implemented-changes-from-jscontact-draft-08)) * [draft-ietf-jmap-jscontact-vcard: JSContact: Converting from and to vCard](https://datatracker.ietf.org/doc/html/draft-ietf-jmap-jscontact-vcard/) * [rfc8984: JSCalendar: A JSON Representation of Calendar Data](https://datatracker.ietf.org/doc/html/rfc8984) +* [links sub-collection to add attachments and links to other application-entries](Links-and-attachments.md) ### Supported request methods and examples diff --git a/doc/REST-CalDAV-CardDAV/Calendar.md b/doc/REST-CalDAV-CardDAV/Calendar.md index 534f130fe7..b9e57697bc 100644 --- a/doc/REST-CalDAV-CardDAV/Calendar.md +++ b/doc/REST-CalDAV-CardDAV/Calendar.md @@ -7,6 +7,7 @@ Authentication is via Basic Auth with username and a password, or a token valid Following RFCs / drafts used/planned for JSON encoding of resources * [rfc8984: JSCalendar: A JSON Representation of Calendar Data](https://datatracker.ietf.org/doc/html/rfc8984) +* [links sub-collection to add attachments and links to other application-entries](Links-and-attachments.md) ### Supported request methods and examples diff --git a/doc/REST-CalDAV-CardDAV/Infolog.md b/doc/REST-CalDAV-CardDAV/Infolog.md new file mode 100644 index 0000000000..6a61e35b8c --- /dev/null +++ b/doc/REST-CalDAV-CardDAV/Infolog.md @@ -0,0 +1,329 @@ +# EGroupware REST API for InfoLog + +Authentication is via Basic Auth with username and a password, or a token valid for: +- either just the given user or all users +- CalDAV/CardDAV Sync (REST API) +- InfoLog application + +Following RFCs / drafts used/planned for JSON encoding of InfoLog entries +* [rfc8984: JSCalendar: A JSON Representation of Calendar Data](https://datatracker.ietf.org/doc/html/rfc8984) +* [links sub-collection to add attachments and links to other application-entries](Links-and-attachments.md) + +### Used JSCalendar Task schema to encode InfoLog entries plus EGroupware&InfoLog specific extensions: +* stock JSCalendar Task attributes like: `uid`, `title`, `start`, ... (see above link) +* `@type` is always `Task` independent of InfoLog type, see `egroupware.org:type` below +* `progress` stock values: + * `needs-action` (InfoLog `not-started`) + * `in-progress` (InfoLog `ongoing`) + * `completed` (InfoLog `done`) + * `cancelled` + * `egroupware.org:` (all other status supported by InfoLog use a `egroupware.org:` prefix) +* `participants` contains task-owner (role `owner`), responsible users (role `attendee`) and CC'ed (role `informational`) + * participants is an object with either the numerical user-id or the email address as attribute-name and an object with the following attributes: + * `name` of owner, responsible or CC'ed + * `email` email-address + * `kind` always `individual` or `group` for groups (other kinds are NOT supported) + * `roles`: are NOT explicitly stored + * `attendee` responsible user or group of the task + * `infomational` CC'ed email addresses to keep informed / send notifications to + * `owner` this is the task owner, which might or might not be also an `attendee` + * other attributes like `participationStatus` are currently NOT persisted by InfoLog +* `egroupware.org:type` InfoLog type like `task`, `phone`, `note`, `email`, also custom InfoLog-types admin has created +* `egroupware.org:completed` completed date&time +* `egroupware.org:price` float value with price, if set +* `egroupware.org:pricelist` integer ID of used price-list (readonly) +* `egroupware.org:customfields` custom-field object, if custom-fields are defined by admin and used in the entry +* `relatedTo` object with UID of parent-entry as attribute-name and object as value with attributes + * `@type` `Relation` + * `relation` and value `parent`, other relation types are NOT supported +* you can use the [`links` sub-collection](Links-and-attachments.md) of each entry to add relations/links to other application-entries +* InfoLogs primary link can also be created via the [`links` sub-collection](Links-and-attachments.md) with a`rel` of `egroupware.org-primary` + +### Supported request methods and examples + +* **GET** to collections with an ```Accept: application/json``` header return all resources (similar to WebDAV PROPFIND) +
+ Example: Getting all entries of a given users infolog collection + +``` +curl https://example.org/egroupware/groupdav.php//infolog/ -H "Accept: application/pretty+json" --user +{ + "responses": { + "//infolog/1085": { + "@type": "Task", + "prodId": "EGroupware InfoLog 23.1.006", + "uid": "infolog-1085-8623c4830472a8ede9f9f8b30d435ea4", + "created": "2020-08-08T13:37:46Z", + "title": "Re: Test creat(ed|or)", + "start": "2020-08-08T00:00:00", + "showWithoutTime": true, + "timeZone": "Europe/Berlin", + "description": "kkk", + "participants": { + "5": { + "@type": "Participant", + "name": "Ralf Becker", + "email": "ralf@example.org", + "kind": "individual", + "roles": { "owner": true } + } + }, + "status": "confirmed", + "progress": "in-progress", + "priority": 9, + "privacy": "public", + "percentComplete": 10, + "egroupware.org:type": "task", + "relatedTo": { + "56f7094e-e962-904d-b74a-cf139f9eecb0": { + "@type": "Relation", + "relation": "parent" + } + } + }, + "//infolog/1081": { + "@type": "Task", + "prodId": "EGroupware InfoLog 23.1.006", + "uid": "infolog-1081-8623c4830472a8ede9f9f8b30d435ea4", + "sequence": "2", + "created": "2020-08-08T13:07:18Z", + "title": "Testtitle", + "start": "2020-08-08T00:00:00", + "showWithoutTime": true, + "timeZone": "Europe/Berlin", + "description": "This is a Test ...", + "participants": { + "44": { + "@type": "Participant", + "name": "Birgit Becker", + "email": "birgit@example.org", + "kind": "individual", + "roles": { "owner": true } + } + }, + "status": "tentative", + "progress": "egroupware.org:offer", + "priority": 9, + "privacy": "public", + "percentComplete": 10, + "egroupware.org:type": "task", + } + } +} +``` +
+ +following GET parameters are supported to customize the returned properties: +- props[]= eg. props[]=getetag to return only the ETAG (multiple DAV properties can be specified) + Default for calendar collections is to only return calendar-data (JsEvent), other collections return all props. +- sync-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!) + this will return a "more-results"=true attribute and a new "sync-token" attribute to query for the next chunk + +Examples: see addressbook + + +* **GET** requests with an ```Accept: application/json``` header can be used to retrieve single resources / JsContact or JsCalendar schema +
+ Example: GET request for a single resource + +``` +curl 'https://example.org/egroupware/groupdav.php/infolog/956' -H "Accept: application/pretty+json" --user +{ + "@type": "Task", + "prodId": "EGroupware InfoLog 23.1.006", + "uid": "infolog-956-8623c4830472a8ede9f9f8b30d435ea4", + "created": "2018-01-31T08:17:07Z", + "title": "Test notification", + "start": "2018-01-31T00:00:00", + "showWithoutTime": true, + "timeZone": "Europe/Berlin", + "description": "Blah sdfasdfa", + "participants": { + "5": { + "@type": "Participant", + "name": "Ralf Becker", + "email": "ralf@example.org", + "kind": "individual", + "roles": { "owner": true } + }, + "181": { + "@type": "Participant", + "name": "Hadi Nategh", + "email": "hn@example.org", + "kind": "individual", + "roles": { "attendee": true } + }, + "44": { + "@type": "Participant", + "name": "Birgit Becker", + "email": "birgit@example.org", + "kind": "individual", + "roles": { "attendee": true } + } + }, + "status": "confirmed", + "progress": "needs-action", + "priority": 9, + "privacy": "public", + "egroupware.org:type": "task", + "egroupware.org:customfields": { + "contact": { + "value": [ + "Internet" + ], + "type": "select", + "label": "Kontakt", + "values": { + "Internet": "Internet", + "Presse": "Presse", + "Zeitschrift": "Zeitschrift", + "Empfehlung": "Empfehlung", + "Hotel": "Hotel", + "Unknown": "Weiß nicht", + "With Space": "With Space" + } + }, + "selection": { + "value": [ + "1" + ], + "type": "select", + "label": "Auswahl", + "values": { + "1": "Hugo", + "2": "Ralf", + "3": "sonstwer" + } + } + } +} + ``` +
+ +* **POST** requests to collection with a ```Content-Type: application/json``` header add new entries in infolog collections + (Location header in response gives URL of new resource) +
+ Example: POST request to create a new resource and use "Prefer: return=representation" to get it fully expanded back + +``` +RalfsMac:mserver ralf$ cat </infolog/' \ + -H "Content-Type: application/json" -H "Prefer: return=representation" -H "Accept: application/pretty+json" \ + -X POST -d @- --user +{ + "title": "Test-Test for Birgit", + "start": "2024-05-10T10:00:00", + "timeZone": "Europe/Berlin", + "duration": "PT1H", + "due": "2024-06-01T00:00:00", + "description": "Important task, but quite short ;)", + "priority": 9, + "participants": [ + { + "name": "Birgit Becker", + "email": "birgit@example.org", + "roles": { "attendee": true } + } + ] +} +EOF + +HTTP/1.1 201 Created +Location: /egroupware/groupdav.php//infolog/1192 +ETag: "1192:0:1715012714" +X-WebDAV-Status: 201 Created + +{ + "@type": "Task", + "prodId": "EGroupware InfoLog 23.1.006", + "uid": "urn:uuid:3933d565-187f-4bad-a44e-82588ef64c88", + "created": "2024-05-06T14:25:14Z", + "title": "Test-Test for Birgit", + "start": "2024-05-10T10:00:00", + "timeZone": "Europe/Berlin", + "due": "2024-06-01T00:00:00", + "duration": "PT1H", + "description": "Important task, but quite short ;)", + "participants": { + "5": { + "@type": "Participant", + "name": "Ralf Becker", + "email": "ralf@example.org", + "kind": "individual", + "roles": { "owner": true } + }, + "44": { + "@type": "Participant", + "name": "Birgit Becker", + "email": "birgit@example.org", + "kind": "individual", + "roles": { "attendee": true } + } + }, + "status": "confirmed", + "progress": "completed", + "privacy": "public", + "egroupware.org:type": "task" +} +``` +
+ +* **PUT** requests with a ```Content-Type: application/json``` header allow modifying single resources (requires to specify all attributes!) + +
+ Example: PUT request with UID to update an existing resource or create it, if not exists + +``` +cat </infolog/1192' -X PUT -d @- -H "Content-Type: application/json" --user +{ + "@type": "Task", + "prodId": "EGroupware InfoLog 23.1.006", + "uid": "urn:uuid:3933d565-187f-4bad-a44e-82588ef64c88", + "created": "2024-05-06T14:25:14Z", + "title": "Test-Test for Birgit updated", +.... +} +EOF +``` +Update of an existing one: +``` +HTTP/1.1 204 No Content +``` +New tast: +``` +HTTP/1.1 201 Created +Location: https://example.org/egroupware/groupdav.php//infolog/1234 +``` +
+ + +* **PATCH** request with a ```Content-Type: application/json``` header allow to modify a single resource by only specifying changed attributes as a [PatchObject](https://www.rfc-editor.org/rfc/rfc8984.html#type-PatchObject) + +
+ Example: PATCH request to modify an event with partial data + +``` +cat </infolog/1234' -X PATCH -d @- -H "Content-Type: application/json" --user +{ + "title": "New title" +} +EOF + +HTTP/1.1 204 No content +``` +
+ +* **DELETE** requests delete single resources +
+ Example: Delete an existing event + +> Please note: the "Accept: application/json" header is required, as the CalDAV server would return 404 NotFound as the url does NOT end with .ics + +``` +curl -i 'https://example.org/egroupware/groupdav.php//infolog/1234' -X DELETE -H "Accept: application/json" --user + +HTTP/1.1 204 No Content +``` +
+ +* one can use ```Accept: application/pretty+json``` to receive pretty-printed JSON e.g. for debugging and exploring the API \ No newline at end of file diff --git a/doc/REST-CalDAV-CardDAV/README.md b/doc/REST-CalDAV-CardDAV/README.md index 3a97e52fab..408f0bd6ef 100644 --- a/doc/REST-CalDAV-CardDAV/README.md +++ b/doc/REST-CalDAV-CardDAV/README.md @@ -43,6 +43,7 @@ from the data of an ```allprop``` PROPFIND, allow browsing CalDAV/CardDAV tree w - [Addressbook](Addressbook.md) - [Calendar](Calendar.md) * currently recurring events are readonly, they are returned but can not be created or modified +- [InfoLog](Infolog.md) - [Mail](Mail.md) * currently only sending mails, * opening interactive compose windows, @@ -66,10 +67,10 @@ Following RFCs / drafts used/planned for JSON encoding of resources ### ToDos - [x] Addressbook - [ ] update of photos, keys, attachments -- [ ] InfoLog +- [X] InfoLog - [X] Calendar (recurring events and alarms are readonly) - [ ] support creating and modifying recurring events and alarms - [X] Mail - [ ] querying received mails -- [ ] relatedTo / links +- [X] relatedTo, links and attachments, see [links sub-collection to add attachments and links to other application-entries](Links-and-attachments.md) - [ ] storing not native supported attributes eg. localization \ No newline at end of file diff --git a/infolog/inc/class.infolog_groupdav.inc.php b/infolog/inc/class.infolog_groupdav.inc.php index ca250c6578..13fa05e4f9 100644 --- a/infolog/inc/class.infolog_groupdav.inc.php +++ b/infolog/inc/class.infolog_groupdav.inc.php @@ -653,7 +653,7 @@ class infolog_groupdav extends Api\CalDAV\Handler $type = null; if (($is_json=Api\CalDAV::isJSON($type))) { - $task = Api\CalDAV\JsCalendar::parseJsTask($options['content'], $oldTask ?? [], $type, $method, $user) + $oldTask??[]; + $task = Api\CalDAV\JsCalendar::parseJsTask($options['content'], $oldTask ?? [], $type, $method, $user) + ($oldTask??[]); if ($callback_data) { $callback = array_shift($callback_data);