diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2094,6 +2094,7 @@ 'PhabricatorCalendarExportViewController' => 'applications/calendar/controller/PhabricatorCalendarExportViewController.php', 'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php', 'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php', + 'PhabricatorCalendarICSWriter' => 'applications/calendar/util/PhabricatorCalendarICSWriter.php', 'PhabricatorCalendarIconSet' => 'applications/calendar/icon/PhabricatorCalendarIconSet.php', 'PhabricatorCalendarRemarkupRule' => 'applications/calendar/remarkup/PhabricatorCalendarRemarkupRule.php', 'PhabricatorCalendarReplyHandler' => 'applications/calendar/mail/PhabricatorCalendarReplyHandler.php', @@ -6866,6 +6867,7 @@ 'PhabricatorCalendarExportViewController' => 'PhabricatorCalendarController', 'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO', 'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase', + 'PhabricatorCalendarICSWriter' => 'Phobject', 'PhabricatorCalendarIconSet' => 'PhabricatorIconSet', 'PhabricatorCalendarRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'PhabricatorCalendarReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', diff --git a/src/applications/calendar/controller/PhabricatorCalendarController.php b/src/applications/calendar/controller/PhabricatorCalendarController.php --- a/src/applications/calendar/controller/PhabricatorCalendarController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarController.php @@ -6,34 +6,11 @@ PhabricatorUser $viewer, $file_name, array $events) { - $events = mpull($events, null, 'getPHID'); - if ($events) { - $child_map = id(new PhabricatorCalendarEventQuery()) - ->setViewer($viewer) - ->withParentEventPHIDs(array_keys($events)) - ->execute(); - $child_map = mpull($child_map, null, 'getPHID'); - } else { - $child_map = array(); - } - - $all_events = $events + $child_map; - $child_groups = mgroup($child_map, 'getInstanceOfEventPHID'); - - $document_node = new PhutilCalendarDocumentNode(); - - foreach ($all_events as $event) { - $child_events = idx($child_groups, $event->getPHID(), array()); - $event_node = $event->newIntermediateEventNode($viewer, $child_events); - $document_node->appendChild($event_node); - } - - $root_node = id(new PhutilCalendarRootNode()) - ->appendChild($document_node); - - $ics_data = id(new PhutilICSWriter()) - ->writeICSDocument($root_node); + $ics_data = id(new PhabricatorCalendarICSWriter()) + ->setViewer($viewer) + ->setEvents($events) + ->writeICSDocument(); return id(new AphrontFileResponse()) ->setDownload($file_name) diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php @@ -309,16 +309,10 @@ PhabricatorCalendarEvent $event) { $actor = $this->getActor(); - $event_node = $event->newIntermediateEventNode($actor); - - $document_node = id(new PhutilCalendarDocumentNode()) - ->appendChild($event_node); - - $root_node = id(new PhutilCalendarRootNode()) - ->appendChild($document_node); - - $ics_data = id(new PhutilICSWriter()) - ->writeICSDocument($root_node); + $ics_data = id(new PhabricatorCalendarICSWriter()) + ->setViewer($actor) + ->setEvents(array($event)) + ->writeICSDocument(); $ics_attachment = new PhabricatorMetaMTAAttachment( $ics_data, diff --git a/src/applications/calendar/controller/PhabricatorCalendarController.php b/src/applications/calendar/util/PhabricatorCalendarICSWriter.php copy from src/applications/calendar/controller/PhabricatorCalendarController.php copy to src/applications/calendar/util/PhabricatorCalendarICSWriter.php --- a/src/applications/calendar/controller/PhabricatorCalendarController.php +++ b/src/applications/calendar/util/PhabricatorCalendarICSWriter.php @@ -1,11 +1,33 @@ viewer = $viewer; + return $this; + } + + public function getViewer() { + return $this->viewer; + } + + public function setEvents(array $events) { + assert_instances_of($events, 'PhabricatorCalendarEvent'); + $this->events = $events; + return $this; + } + + public function getEvents() { + return $this->events; + } + + public function writeICSDocument() { + $viewer = $this->getViewer(); + $events = $this->getEvents(); - protected function newICSResponse( - PhabricatorUser $viewer, - $file_name, - array $events) { $events = mpull($events, null, 'getPHID'); if ($events) { @@ -32,13 +54,7 @@ $root_node = id(new PhutilCalendarRootNode()) ->appendChild($document_node); - $ics_data = id(new PhutilICSWriter()) + return id(new PhutilICSWriter()) ->writeICSDocument($root_node); - - return id(new AphrontFileResponse()) - ->setDownload($file_name) - ->setMimeType('text/calendar') - ->setContent($ics_data); } - } diff --git a/src/docs/user/userguide/calendar_exports.diviner b/src/docs/user/userguide/calendar_exports.diviner --- a/src/docs/user/userguide/calendar_exports.diviner +++ b/src/docs/user/userguide/calendar_exports.diviner @@ -9,4 +9,89 @@ IMPORTANT: Calendar is a prototype application. See @{article:User Guide: Prototype Applications}. -Coming soon! +You can export events from Phabricator to other calendar applications like +**Google Calendar** or **Calendar.app**. This document will guide you through +how to export event data from Phabricator. + +When you export events into another application, they generally will not be +editable from that application. Exporting events allows you to create one +calendar that shows all the events you care about in whatever application you +prefer (so you can keep track of everything you need to do), but does not let +you edit Phabricator events from another application. + +When exporting events, you can either export individual events one at a time +or export an entire group of events (for example, all events you are attending). + + +Exporting a Single Event +======================== + +To export a single event, visit the event detail page and click +{nav Export as .ics}. This will download an `.ics` file which you can import +into most other calendar applications. + +Mail you receive about events also has a copy of this `.ics` file attached to +it. You can import this `.ics` file directly. + +In **Google Calendar**, use {nav Other Calendars > Import Calendar} to import +the `.ics` file. + +In **Calendar.app**, use {nav File > Import...} to import the `.ics` file, or +drag the `.ics` file onto your calendar. + +When you export a recurring event, the `.ics` file will contain information +about the entire event series. + +If you want to update event information later, you can just repeat this +process. Calendar applications will update the existing event if you've +previously imported an older version of it. + + +Exporting a Group of Events +=========================== + +You can export a group of events matching an arbitrary query (like all events +you are attending) to keep different calendars in sync. + +To export a group of events: + + - Run a query in Calendar which selects the events you want to export. + - Example: All events you are attending. + - Example: All events you are invited to. + - Example: All events tagged `#meetup`. + - Select the {nav Use Results... > Export Query as .ics} action to turn + the query into an export. + - Name the export with a descritive name. + - Select a policy mode for the export (see below for discussion). + - Click {nav Create New Export} to finish the process. + +The **policy modes** for exports are: + + - **Public**: Only public information (visible to logged-out users) will + be exported. This mode is not available if your install does not have + public information (per `policy.allow-public` in Config). + - **Privileged**: All event information will be exported. This means that + anyone who knows the export URI can see ALL of the related event + information, as though they were logged in with your account. + +WARNING: Anyone who learns the URI for an export can see the data you choose +to export, even if they don't have a Phabricator account! Be careful about how +much data you export and treat the URI as a secret. If you accidentally share +a URI, you can disable the export. + +After finishing the process, you'll see a screen with some details about the +export and an **ICS URI**. This URI allows you to import the events which match +the query into another calendar application. + +In **Google Calendar**, use {nav Other Calendars > Add by URL} to import the +URI. + +In **Calendar.app**, use {nav File > New Calendar Subscription...} to subscribe +to the URI. + +Next Steps +========== + +Continue by: + + - returning to the @{article:Calendar User Guide}.