Content
View differences
Updated by Marc Alcobé over 3 years ago
# User Story
**As a** project team member
**I want t**o see my project calendar in my personal calendar
**So that** I don't have to maintain two different calendars to organize my work.
# Use case examples
1. Release dates and other project milestones
2. Submission deadlines of artefacts that require collaboration with other team members
3. Extend certificats (e.g. SSL, SMIME)
While trying to introduce OpenProject in a team that I’m part of, I realised that there is no way to integrate the calendar with MS Outlook, Apple Calendar, Google Calendar, and the likes. It would greatly increase the visibility of project planning if people were able to see changes in their preferred calendar apps, instead of having to check the OpenProject calendar webpage. Export functionality would provide a very natural means to integrate OpenProject with existing planning and coordination routines established in teams (most of which will be using a calendar app of sorts).
# Acceptance criteria
* Separate project permission
* Menu entry to create and copy URL
* Caching
* The calendar invite includes the main information of the workpackage
* The iCalendar-token can be revoked in "My account -> Access tokens"
# Attribute mapping
<figure class="table op-uc-figure_align-center op-uc-figure"><table class="op-uc-table"><thead class="op-uc-table--head"><tr class="op-uc-table--row"><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Example</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Work package attribute</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">mapped iCal ics attribute</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">iCal notes</th></tr></thead><tbody><tr class="op-uc-table--row"><td op-uc-table--cell_head">OX UI</th><th class="op-uc-p op-uc-table--cell">OpenProject GmbH//OpenProject Core Project//EN</td><td op-uc-table--cell op-uc-table--cell_head">Thunderbird UI</th><th class="op-uc-p op-uc-table--cell">Container calendar and company</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-7-3-product-identifier.html">PRODID</a></td><td class="op-uc-p op-uc-table--cell">This is a MUST in iCal and specifies who created this iCal and for what and its never represented in the UI</td></tr><tr op-uc-table--cell op-uc-table--cell_head">iOS UI</th></tr></thead><tbody><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">V1.0</td><td class="op-uc-p op-uc-table--cell">Version of the iCal</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-7-4-version.html">VERSION</a></td><td class="op-uc-p op-uc-table--cell">This is the version of iCal and is a MUST and it is not represented in the UI</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">-</td><td class="op-uc-p op-uc-table--cell">Work package (type of calendar entry)</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-6-3-journal-component.html">VJOURNAL</a></td><td class="op-uc-p op-uc-table--cell">After analysis of all the iCal attributes the most logical seems to make the WP transform into a VJOURNAL which is basically a Journal entry without need to be a meeting or a event.</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:id @community.openproject.com</td><td class="op-uc-p op-uc-table--cell">Calendar entry ID</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-4-7-unique-identifier.html">UID</a> (id)</td><td class="op-uc-p op-uc-table--cell">This is a MUST in iCAL and must be unique for each calendar entry in all the iCal and therefore is recommended to use specific methods of creation <a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-4-7-unique-identifier.html">explained here</a></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:id</td><td class="op-uc-p op-uc-table--cell">Work package ID</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-8-1-iana-properties.html">IANA</a> (text)</td><td class="op-uc-p op-uc-table--cell">This can be added in the calendar entry as a "IANA" extra property. All compliant applications are expected to be able to parse but it can be that they are ignored.</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://community.openproject.org/projects/openproject/work_packages/15339">.../openproject/work_packages/15339</a></td><td class="op-uc-p op-uc-table--cell">Work package URL</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-4-6-uniform-resource-locator.html">URL</a> (link)</td><td class="op-uc-p op-uc-table--cell">Not clear if this information is shared with the iCal "URI" or should be included as an ATTACH to be displayed (or in the description)</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:subject</td><td class="op-uc-p op-uc-table--cell">Subject</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-12-summary.html">SUMMARY</a> (text)</td><td op-uc-table--cell">SUMMARY</td><td class="op-uc-p op-uc-table--cell">The subject could be directly matched as the "SUMMARY" of the calendar entry</td></tr><tr class="op-uc-table--row"><td op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell">Delivering the first version of the new fancy product.</td><td op-uc-table--cell">Title</td><td class="op-uc-p op-uc-table--cell">Description</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-5-description.html">DESCRIPTION</a> (text)</td><td class="op-uc-p op-uc-table--cell">The description is always displayed in the calendar entries. We need to decide what is the content we want to display in this description as I assume we don't want to show the full WP description here.</td></tr><tr op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:status</td><td class="op-uc-p op-uc-table--cell">Status</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-2-categories.html">CATEGORIES</a> (text)</td><td class="op-uc-p op-uc-table--cell">"STATUS" is limited to the type of entry journal and there is not a lot of options, <a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-11-status.html">see here</a></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:type</td><td class="op-uc-p op-uc-table--cell">Work package type</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-2-categories.html">CATEGORIES</a> (text)</td><td class="op-uc-p op-uc-table--cell">iCal "TYPE" will not work here as it specifies the type of calendar not of each entry.</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:priority</td><td class="op-uc-p op-uc-table--cell">Priority</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-9-priority.html">PRIORITY</a> (integer)</td><td class="op-uc-p op-uc-table--cell">"PRIORITY" can be used to see the priority in between the elements of a calendar and they are defined by numbers form 0 to 9 that can be ranged also as LOW, MEDIUM and HIGH</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:author</td><td class="op-uc-p op-uc-table--cell">Author</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-4-3-organizer.html">ORGANIZER</a> (cal-address) or <a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-4-2-contact.html">CONTACT</a> (text)</td><td class="op-uc-table--cell"><p class="op-uc-p">If we extract the Author and map it as Organizer this could appear in the iCal invite</p><p class="op-uc-p">This is even possible with LDAP URI</p></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:assignee</td><td class="op-uc-p op-uc-table--cell">Assignee</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-4-2-contact.html">CONTACT</a> (text)</td><td class="op-uc-p op-uc-table--cell">We could use the assignee and map it with the "CONTACT" reference in the calendar entry. This is even possible with LDAP URI. But cannot be used if used for Author</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">20.01.2023</td><td class="op-uc-p op-uc-table--cell">Creation date</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-7-1-date-time-created.html">CREATED</a> (date-time)</td><td class="op-uc-p op-uc-table--cell">Maps the WP creation date with</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">20.01.2023</td><td class="op-uc-p op-uc-table--cell">Last modification date</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-7-3-last-modified.html">LAST-MODIFIED</a> (date-time)</td><td class="op-uc-p op-uc-table--cell">We could map the last modification date in the activity with the "LAST-MODIFIED"</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:startDate</td><td class="op-uc-p op-uc-table--cell">Start date</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-2-4-date-time-start.html">DTSTART</a> (date)</td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:dueDate</td><td class="op-uc-p op-uc-table--cell">Finish date</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-2-2-date-time-end.html">DTEND</a> or <a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-2-3-date-time-due.html">DUE</a> (date)</td><td class="op-uc-p op-uc-table--cell">Need to see what works better if "DTEND" or "DUE"</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:duration</td><td class="op-uc-p op-uc-table--cell">Duration</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-3-6-duration.html">DURATION</a> (duration)</td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell"><mention class="mention" data-id="25541" data-type="work_package" data-text="#25541">#25541</mention> </td><td class="op-uc-p op-uc-table--cell">Parent work package</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-4-5-related-to.html">RELATED-TO</a></td><td class="op-uc-p op-uc-table--cell">Can be used to establish if the work package is related to any other calendar entries (WP)</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">-</td><td class="op-uc-p op-uc-table--cell">Any other information</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-4-comment.html">COMMENT</a> (text)</td><td class="op-uc-p op-uc-table--cell">Everything can be always added to an extra section besides the description called "COMMENT"</td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">-</td><td class="op-uc-p op-uc-table--cell">Extra custom values</td><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-8-1-iana-properties.html">IANA</a> (text) or <a class="op-uc-link" href="https://icalendar.org/iCalendar-RFC-5545/3-8-8-2-non-standard-properties.html">X-</a> (text with other type acceptance)</td><td class="op-uc-p op-uc-table--cell">This are extra full custom properties that all compliant applications are expected to be able to parse but it can be that they are ignored.</td></tr></tbody></table></figure>
# Ideal iCal example
Using this work package as example the journal entry in a iCalendar ideal format should be:
```text
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//OpenProject GmbH//OpenProject Core Project//EN
BEGIN:VJOURNAL
DTSTAMP:20230120T000000
CREATED:20230120T151920
LAST-MODIFIED:20230120T183250
DTSTART:20230112T000000
DTEND:20230415T000000
DURATION:P94D
UID:15339@community.openproject.com
WPID:15339
URL:https://community.openproject.org/projects/
openproject/work_packages/15339
ORGANIZER;CN=Tobias Nobauer:mailto:t.nobauer@openproject.com
CONTACT:Jonas Jabari\, OpenProject GmbH\, mailto:j.jabari@openproject.com
SUMMARY:Share project calendars using the iCalendar format
DESCRIPTION:Work package description:/User Story/
As a project team member/I want to see my project
calendar in my personal calendar/So that I don't
have to maintain two different calendars to organize
my work.
CATEGORIES:🟩 FEATURE,In development
PRIORITY:5
RELATED-TO:25541@community.openproject.com
END:VJOURNAL
END:VCALENDAR
```
This code has been successfully validated using the [iCalendar Validator](https://icalendar.org/validator.html#results) and it has no errors.
# Calendar clients UI
<figure class="table op-uc-figure_align-center op-uc-figure"><table class="op-uc-table"><thead class="op-uc-table--head"><tr class="op-uc-table--row"><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Example</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Work package attribute</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">OX</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Thunderbird</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Mailbox</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Outlook</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Yahoo</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Google Calendar</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Google Calendar (Android)</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Calendar (MAC)</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Calendar (iOS)</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Basecamp</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">30 Boxes</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Trumba</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">BusyCal</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">OneCalendar</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Fantastical</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Structured</th></tr></thead><tbody><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">OpenProject GmbH//OpenProject Core Project//EN</td><td class="op-uc-p op-uc-table--cell">Container calendar and company</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">V1.0</td><td op-uc-table--cell">workPackageValue:15339:author</td><td class="op-uc-p op-uc-table--cell">Version of the iCal</td><td op-uc-table--cell">Author</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td op-uc-table--cell">Status</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">-</td><td class="op-uc-p op-uc-table--cell">Work package (type of calendar entry)</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:id @community.openproject.com</td><td class="op-uc-p op-uc-table--cell">Calendar entry ID</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:id</td><td class="op-uc-p op-uc-table--cell">Work package ID</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell"><a class="op-uc-link" href="https://community.openproject.org/projects/openproject/work_packages/15339">.../openproject/work_packages/15339</a></td><td class="op-uc-p op-uc-table--cell">Work package URL</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:subject</td><td class="op-uc-p op-uc-table--cell">Subject</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">Delivering the first version of the new fancy product.</td><td class="op-uc-p op-uc-table--cell">Description</td><td class="op-uc-p op-uc-table--cell"></td><td op-uc-table--cell">DESCRIPTION</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:status</td><td op-uc-table--cell">workPackageValue:15339:startDate</td><td class="op-uc-p op-uc-table--cell">Status</td><td op-uc-table--cell">Start date</td><td class="op-uc-p op-uc-table--cell"></td><td op-uc-table--cell">DTSTART</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td op-uc-table--cell">Start Date</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:type</td><td op-uc-table--cell">workPackageValue:15339:dueDate</td><td class="op-uc-p op-uc-table--cell">Work package type</td><td op-uc-table--cell">Finish date</td><td class="op-uc-p op-uc-table--cell"></td><td op-uc-table--cell">DTEND</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td op-uc-table--cell">End Date</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:priority</td><td class="op-uc-p op-uc-table--cell">Priority</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:author</td><td op-uc-table--cell">workPackageValue:15339:type</td><td class="op-uc-p op-uc-table--cell">Author</td><td op-uc-table--cell">Work package type</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:assignee</td><td op-uc-table--cell">workPackageValue:15339:id</td><td class="op-uc-p op-uc-table--cell">Assignee</td><td op-uc-table--cell">Work package ID</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">20.01.2023</td><td class="op-uc-p op-uc-table--cell">Creation date</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td op-uc-table--cell">LOCATION</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">20.01.2023</td><td class="op-uc-p op-uc-table--cell">Last modification date</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td op-uc-table--cell">Work package URL</td><td class="op-uc-p op-uc-table--cell"></td><td op-uc-table--cell">URL</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:startDate</td><td class="op-uc-p op-uc-table--cell">Start date</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td op-uc-table--cell">Calendar title (query title)</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:dueDate</td><td class="op-uc-p op-uc-table--cell">Finish date</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">workPackageValue:15339:duration</td><td class="op-uc-p op-uc-table--cell">Duration</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell"><mention class="mention" data-id="25541" data-type="work_package" data-text="#25541">#25541</mention> </td><td class="op-uc-p op-uc-table--cell">Parent work package</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">-</td><td class="op-uc-p op-uc-table--cell">Any other information</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">-</td><td class="op-uc-p op-uc-table--cell">Extra custom values</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr></tbody></table></figure>
# Figma link
https://www.figma.com/file/fIFszjTJWyd0p94SXjih58/Calendar-view?node-id=111%3A7801
# ToDo
* [x] Investigate all the possibilities of iCal and what can we display:
* [x] If we can use the field "status" for any work package status such as "scheduled"
* [x] Create an ideal example of iCal txt
* [ ] Benchmark on how are the iCal parameters displayed in the calendar clients Update mockups
* [ ] OX [x] URL: [https://openproject.example.com/projects/demo-project/calendars/27.ics?ical\_token=c6ec6fea407cb0eca52db380daefacbe](https://openproject.example.com/projects/demo-project/calendars/27.ics?ical_token=c6ec6fea407cb0eca52db380daefacbe)
* [ ] Thunderbird Screen to revoke all tokens (not separately)
* [ ] Mailbox
* [ ] Outlook
* [ ] Yahoo
* [ ] Google Calendar
entry
* [ ] Google Calendar (Android)
OX
* [ ] Calendar (Mac)
Thunderbird
* [ ] Calendar (iOS)
iOS
* [ ] Basecamp Android
* [ ] 30 Boxes
* [ ] Trumba
* [ ] BusyCal
* [ ] OneCalendar
* [ ] Fantastical
* [ ] Structured
* [ ] Update mockups
* [x] URL: [https://openproject.example.com/projects/demo-project/calendars/27.ics?ical\_token=c6ec6fea407cb0eca52db380daefacbe](https://openproject.example.com/projects/demo-project/calendars/27.ics?ical_token=c6ec6fea407cb0eca52db380daefacbe)
* [ ] Screen to revoke all tokens (not separately)
* [ ] Permissions screen (admin settings)
* [ ] Calendar entry in multiple calendar clients
* [x] Add link to mockup here: https://www.figma.com/file/fIFszjTJWyd0p94SXjih58/Calendar-view?node-id=111%3A7801
* [ ] Specify permissions
* [ ] Investigate the capabilities to use emojis to visualise content
* [ ] Work package type
* [ ] Status
* [ ] Priority
* [ ] Investigate if we can use the field "status" for any work package status such as "scheduled"
# Out of scope
* CalDAV (we focus on ics-files for now)
* Update calendar entries in the calendar applications (read-only, no bi-directional sync)
**As a** project team member
**I want t**o see my project calendar in my personal calendar
**So that** I don't have to maintain two different calendars to organize my work.
# Use case examples
1. Release dates and other project milestones
2. Submission deadlines of artefacts that require collaboration with other team members
3. Extend certificats (e.g. SSL, SMIME)
While trying to introduce OpenProject in a team that I’m part of, I realised that there is no way to integrate the calendar with MS Outlook, Apple Calendar, Google Calendar, and the likes. It would greatly increase the visibility of project planning if people were able to see changes in their preferred calendar apps, instead of having to check the OpenProject calendar webpage. Export functionality would provide a very natural means to integrate OpenProject with existing planning and coordination routines established in teams (most of which will be using a calendar app of sorts).
# Acceptance criteria
* Separate project permission
* Menu entry to create and copy URL
* Caching
* The calendar invite includes the main information of the workpackage
* The iCalendar-token can be revoked in "My account -> Access tokens"
# Attribute mapping
<figure class="table op-uc-figure_align-center op-uc-figure"><table class="op-uc-table"><thead class="op-uc-table--head"><tr class="op-uc-table--row"><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Example</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Work package attribute</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">mapped iCal
# Ideal iCal example
Using this work package as example the journal entry in a iCalendar ideal format should be:
```text
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//OpenProject GmbH//OpenProject Core Project//EN
BEGIN:VJOURNAL
DTSTAMP:20230120T000000
CREATED:20230120T151920
LAST-MODIFIED:20230120T183250
DTSTART:20230112T000000
DTEND:20230415T000000
DURATION:P94D
UID:15339@community.openproject.com
WPID:15339
URL:https://community.openproject.org/projects/
openproject/work_packages/15339
ORGANIZER;CN=Tobias Nobauer:mailto:t.nobauer@openproject.com
CONTACT:Jonas Jabari\, OpenProject GmbH\, mailto:j.jabari@openproject.com
SUMMARY:Share project calendars using the iCalendar format
DESCRIPTION:Work package description:/User Story/
As a project team member/I want to see my project
calendar in my personal calendar/So that I don't
have to maintain two different calendars to organize
my work.
CATEGORIES:🟩 FEATURE,In development
PRIORITY:5
RELATED-TO:25541@community.openproject.com
END:VJOURNAL
END:VCALENDAR
```
This code has been successfully validated using the [iCalendar Validator](https://icalendar.org/validator.html#results) and it has no errors.
# Calendar clients UI
<figure class="table op-uc-figure_align-center op-uc-figure"><table class="op-uc-table"><thead class="op-uc-table--head"><tr class="op-uc-table--row"><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Example</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Work package attribute</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">OX</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Thunderbird</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Mailbox</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Outlook</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Yahoo</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Google Calendar</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Google Calendar (Android)</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Calendar (MAC)</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Calendar (iOS)</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Basecamp</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">30 Boxes</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Trumba</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">BusyCal</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">OneCalendar</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Fantastical</th><th class="op-uc-p op-uc-table--cell op-uc-table--cell_head">Structured</th></tr></thead><tbody><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">OpenProject GmbH//OpenProject Core Project//EN</td><td class="op-uc-p op-uc-table--cell">Container calendar and company</td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td><td class="op-uc-p op-uc-table--cell"></td></tr><tr class="op-uc-table--row"><td class="op-uc-p op-uc-table--cell">V1.0</td><td
# Figma link
https://www.figma.com/file/fIFszjTJWyd0p94SXjih58/Calendar-view?node-id=111%3A7801
# ToDo
* [x] Investigate all the possibilities of iCal and what can we display:
* [x] If we can use the field "status" for any work package status such as "scheduled"
* [x] Create an ideal example of iCal txt
* [ ] Benchmark on how are the iCal parameters displayed in the calendar clients
* [ ] OX
* [ ] Thunderbird
* [ ] Mailbox
* [ ] Outlook
* [ ] Yahoo
* [ ] Google Calendar
* [ ] 30 Boxes
* [ ] Trumba
* [ ] BusyCal
* [ ] OneCalendar
* [ ] Fantastical
* [ ] Structured
* [ ] Update mockups
* [x] URL: [https://openproject.example.com/projects/demo-project/calendars/27.ics?ical\_token=c6ec6fea407cb0eca52db380daefacbe](https://openproject.example.com/projects/demo-project/calendars/27.ics?ical_token=c6ec6fea407cb0eca52db380daefacbe)
* [ ] Screen to revoke all tokens (not separately)
* [ ] Permissions screen (admin settings)
* [ ] Calendar entry in multiple calendar clients
* [x] Add link to mockup here: https://www.figma.com/file/fIFszjTJWyd0p94SXjih58/Calendar-view?node-id=111%3A7801
* [ ] Specify permissions
* [ ] Investigate the capabilities to use emojis to visualise content
* [ ] Work package type
* [ ] Status
* [ ] Priority
* [ ] Investigate if we can use the field "status" for any work package status such as "scheduled"
# Out of scope
* CalDAV (we focus on ics-files for now)
* Update calendar entries in the calendar applications (read-only, no bi-directional sync)