diff --git a/CHANGELOG.md b/CHANGELOG.md index 607cf42..4006479 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## 0.3.0 +- Render `VCalendar`, `VEvent` instances, etc just by calling their `toString()` method +- Set any properties +- Set any propery parameters +- Easily generate invites with `VCalendar.createEvent(...)` +- Support any `VEvent` specific [iTIP / RFC 5546](https://datatracker.ietf.org/doc/html/rfc5546) functions: + - change participant status (accept, decline, delegated) with `VCalendar.replyWithParticipantStatus(...)` + - delegate to another attendee with `VCalendar.delegate(...)` + - create a counter proposal with `VCalendar.counter(...)` + - accept a counter proposal with `VCalendar.acceptCounter(...)` + - reject a counter proposal with `VCalendar.declineCounter(...)` + - cancel an event for all with `VCalendar.cancelEvent(...)` + - cancel an event for specific attendees with `VCalendar.cancelEventForAttendees(...)` +- Improve documentation + ## 0.2.0 - Improve documentation - Renamed `Component` to `VComponent` for clarity diff --git a/README.md b/README.md index f0e1935..1d5ab4b 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,34 @@ # enough_icalendar -icalendar library in pure Dart. Fully compliant with [RFC 5545](https://datatracker.ietf.org/doc/html/rfc5545). +iCalendar library in pure Dart. Fully compliant with the iCalendar standard [RFC 5545](https://datatracker.ietf.org/doc/html/rfc5545) and compliant to all VEvent functions of [iTIP / RFC 5546](https://datatracker.ietf.org/doc/html/rfc5546). + +## Installation +Add this dependency your pubspec.yaml file: + +``` +dependencies: + enough_icalendar: ^0.3.0 +``` +The latest version or `enough_icalendar` is [![enough_icalendar version](https://img.shields.io/pub/v/enough_icalendar.svg)](https://pub.dartlang.org/packages/enough_icalendar). + + + +## API Documentation +Check out the full API documentation at https://pub.dev/documentation/enough_icalendar/latest/ ## Usage -Using `enough_icalendar` is pretty straight forward: +Use `enough_icalendar` to parse, generate and respond to iCalendar requests. + +### Import ```dart import 'package:enough_icalendar/enough_icalendar.dart'; +``` +### Parse iCalendar Requests +Use `VComponent.parse(String)` to parse the specified text. -void main() { - final text = '' final text = '''BEGIN:VCALENDAR +```dart + final text = '''BEGIN:VCALENDAR VERSION:2.0 PRODID:-//hacksw/handcal//NONSGML v1.0//EN BEGIN:VEVENT @@ -24,7 +43,7 @@ END:VEVENT END:VCALENDAR'''; final icalendar = VComponent.parse(text) as VCalendar; print(icalendar.productId); // -//hacksw/handcal//NONSGML v1.0//EN - final event = icalendar.children.first as VEvent; + final event = icalendar.event!; print(event.summary); // Bastille Day Party print(event.start); // 1997-06-14 at 17:00 print(event.end); // 1997-07-15 at 03:59:59 @@ -34,22 +53,226 @@ END:VCALENDAR'''; print(event.geoLocation?.longitude); // 2.36885 } ``` +### Generate an invite +Use `VCalendar.createEvent(...)` to create a new invite easily. -## Installation -Add this dependency your pubspec.yaml file: +Alternatively, for full low-lewel control instantiate `VCalendar` yourself and add `VComponent` children as you need. +Add any properties to the components to fill it with live. +Call the `toString()` method to render your invite. +```dart + final invite = VCalendar.createEvent( + organizerEmail: 'a@example.com', + attendeeEmails: ['a@example.com', 'b@example.com', 'c@example.com'], + rsvp: true, + start: DateTime(2021, 07, 21, 10, 00), + end: DateTime(2021, 07, 21, 11, 00), + location: 'Big meeting room', + url: Uri.parse('https://enough.de'), + summary: 'Discussion', + description: + 'Let us discuss how to proceed with the enough_icalendar development. It seems that basic functionality is now covered. What\'s next?', + productId: 'enough_icalendar/v1', + ); + print(invite); + return invite; + // prints this: + // + // BEGIN:VCALENDAR + // PRODID:enough_icalendar/v1 + // VERSION:2.0 + // METHOD:REQUEST + // BEGIN:VEVENT + // DTSTAMP:20210719T090527 + // UID:RQPhszGcPqYFR4fRUT@example.com + // DTSTART:20210721T100000 + // DTEND:20210721T110000 + // ORGANIZER:mailto:a@example.com + // SUMMARY:Discussion + // DESCRIPTION:Let us discuss how to proceed with the enough_icalendar deve + // lopment. It seems that basic functionality is now covered. What's next? + // LOCATION:Big meeting room + // URL:https://enough.de + // ATTENDEE;RSVP=TRUE:mailto:a@example.com + // ATTENDEE;RSVP=TRUE:mailto:b@example.com + // ATTENDEE;RSVP=TRUE:mailto:c@example.com + // END:VEVENT + // END:VCALENDAR ``` -dependencies: - enough_icalendar: ^0.2.0 +### Accept or Decline an Invite +Attendees can change their participant status with `VCalendar.replyWithParticipantStatus(...)`. You either need +to specify the `attendeeEmail` or the `attendee` parameter. This reply will need to be sent to the organizer. +```dart + final reply = invite.replyWithParticipantStatus(ParticipantStatus.accepted, + attendeeEmail: 'b@example.com'); + print(reply); + // prints this: + // + // BEGIN:VCALENDAR + // PRODID:enough_icalendar + // VERSION:2.0 + // METHOD:REPLY + // BEGIN:VEVENT + // ORGANIZER:mailto:a@example.com + // UID:jovSCDXQ3sI5mBuu32@example.com + // ATTENDEE;PARTSTAT=ACCEPTED:mailto:b@example.com + // DTSTAMP:20210719T093653 + // REQUEST-STATUS:2.0;Success + // END:VEVENT ``` -The latest version or `enough_icalendar` is [![enough_icalendar version](https://img.shields.io/pub/v/enough_icalendar.svg)](https://pub.dartlang.org/packages/enough_icalendar). +### Delegate Event Participation +Attendees can delegate their event particpation to others by calling `VCalendar.delegate(...)`. Two results are generated, +one iCalendar for the delegatee and another one for the organizer. +```dart + final delegationResult = original.delegate( + fromEmail: 'c@example.com', + toEmail: 'e@example.com', + ); + print(delegationResult.requestForDelegatee); + // prints this: + // + // BEGIN:VCALENDAR + // PRODID:enough_icalendar/v1 + // VERSION:2.0 + // METHOD:REQUEST + // BEGIN:VEVENT + // DTSTAMP:20210719T173821 + // UID:RQPhszGcPqYFR4fRUT@example.com + // DTSTART:20210721T100000 + // DTEND:20210721T110000 + // ORGANIZER:mailto:a@example.com + // SUMMARY:Discussion + // DESCRIPTION:Let us discuss how to proceed with the enough_icalendar deve + // lopment. It seems that basic functionality is now covered. What's next? + // LOCATION:Big meeting room + // URL:https://enough.de + // ATTENDEE;RSVP=TRUE:mailto:a@example.com + // ATTENDEE;RSVP=TRUE:mailto:b@example.com + // ATTENDEE;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example.com":mailto:c + // @example.com + // ATTENDEE;DELEGATED-FROM="mailto:c@example.com";RSVP=TRUE:mailto:e@exampl + // e.com + // END:VEVENT + // END:VCALENDAR + print(delegationResult.replyForOrganizer); + // prints this: + // + // BEGIN:VCALENDAR + // PRODID:enough_icalendar + // VERSION:2.0 + // METHOD:REPLY + // BEGIN:VEVENT + // ORGANIZER:mailto:a@example.com + // UID:RQPhszGcPqYFR4fRUT@example.com + // ATTENDEE;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example.com":mailto:c + // @example.com + // DTSTAMP:20210719T173821 + // REQUEST-STATUS:2.0;Success + // END:VEVENT + // END:VCALENDAR +``` -## API Documentation -Check out the full API documentation at https://pub.dev/documentation/enough_icalendar/latest/ - - +### Creating and Reponding to Counter Proposals +Attendees can create counter proposals and organizers can accept or decline such proposals. +#### Create a Counter Proposals +Attendees can create counter proposals with `VCalendar.counter(...)`: +```dart + final counterProposal = invite.counter( + comment: 'This time fits better, also we need some more time.', + start: DateTime(2021, 07, 23, 10, 00), + end: DateTime(2021, 07, 23, 12, 00), + location: 'Carnegie Hall', + ); + print(counterProposal); + // prints this: + // + // BEGIN:VCALENDAR + // PRODID:enough_icalendar/v1 + // VERSION:2.0 + // METHOD:COUNTER + // BEGIN:VEVENT + // UID:RQPhszGcPqYFR4fRUT@example.com + // ORGANIZER:mailto:a@example.com + // SUMMARY:Discussion + // DESCRIPTION:Let us discuss how to proceed with the enough_icalendar deve + // lopment. It seems that basic functionality is now covered. What's next? + // URL:https://enough.de + // ATTENDEE;RSVP=TRUE:mailto:a@example.com + // ATTENDEE;RSVP=TRUE:mailto:b@example.com + // ATTENDEE;RSVP=TRUE:mailto:c@example.com + // DTSTAMP:20210719T142550 + // COMMENT:This time fits better, also we need some more time. + // LOCATION:Carnegie Hall + // DTSTART:20210723T100000 + // DTEND:20210723T120000 + // END:VEVENT + // END:VCALENDAR +``` +#### Accept a Counter Proposal +Organizers can accept a counter proposal with `VCalendar.acceptCounter(...)`. +The accepted proposal will have a higher sequence and the status automatically be set to EventStatus.confirmed. +The accepted and update invite is to be sent to all attendees by the organizer. +```dart + final accepted = counterProposal.acceptCounter( + comment: 'Accepted this proposed change of date and time'); + print(accepted); + // prints this: + // + // BEGIN:VCALENDAR + // PRODID:enough_icalendar/v1 + // VERSION:2.0 + // METHOD:REQUEST + // BEGIN:VEVENT + // UID:RQPhszGcPqYFR4fRUT@example.com + // ORGANIZER:mailto:a@example.com + // SUMMARY:Discussion + // DESCRIPTION:Let us discuss how to proceed with the enough_icalendar deve + // lopment. It seems that basic functionality is now covered. What's next? + // URL:https://enough.de + // ATTENDEE;RSVP=TRUE:mailto:a@example.com + // ATTENDEE;RSVP=TRUE:mailto:b@example.com + // ATTENDEE;RSVP=TRUE:mailto:c@example.com + // LOCATION:Carnegie Hall + // DTSTART:20210723T100000 + // DTEND:20210723T120000 + // SEQUENCE:1 + // DTSTAMP:20210719T143344 + // STATUS:CONFIRMED + // COMMENT:Accepted this proposed change of date and time + // END:VEVENT + // END:VCALENDAR +``` +#### Decline a Counter Proposal +Organizers can decline a counter proposal with `VCalendar.declineCounter(...)`. The declined reply is to be sent to the proposing attendee. +```dart + final declined = counterProposal.declineCounter( + attendeeEmail: 'b@example.com', + comment: 'Sorry, but we have to stick to the original schedule'); + print(declined); + // prints this: + // + // BEGIN:VCALENDAR + // PRODID:enough_icalendar/v1 + // VERSION:2.0 + // METHOD:DECLINECOUNTER + // BEGIN:VEVENT + // UID:vmScK-AyJr0NX2nCsW@example.com + // ORGANIZER:mailto:a@example.com + // SUMMARY:Discussion + // DESCRIPTION:Let us discuss how to proceed with the enough_icalendar deve + // lopment. It seems that basic functionality is now covered. What's next? + // URL:https://enough.de + // ATTENDEE;RSVP=TRUE:mailto:b@example.com + // DTSTAMP:20210719T143715 + // LOCATION:Carnegie Hall + // DTSTART:20210723T100000 + // DTEND:20210723T120000 + // COMMENT:Sorry, but we have to stick to the original schedule + // END:VEVENT + // END:VCALENDAR +``` ## Features and bugs `enough_icalendar` supports all icalendar components and provides easy to access models: @@ -60,6 +283,8 @@ Check out the full API documentation at https://pub.dev/documentation/enough_ica * `VFREEBUSY` * `VTODO` * `VJOURNAL` +* Fully compliant with the iCalendar standard [RFC 5545](https://datatracker.ietf.org/doc/html/rfc5545) +* Compliant to all `VEvent` functions of [iTIP / RFC 5546](https://datatracker.ietf.org/doc/html/rfc5546). Please file feature requests and bugs at the [issue tracker][tracker]. diff --git a/pubspec.yaml b/pubspec.yaml index 499bea2..fa36adf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: enough_icalendar -description: icalendar library in pure Dart. Fully compliant with RFC 5545. -version: 0.2.0 +description: iCalendar library in pure Dart. Fully compliant with RFC 5545 (iCalendar) and RFC 5546 (iTIP). +version: 0.3.0 homepage: https://github.com/Enough-Software/enough_icalendar environment: