From 47debbd57c85c7affeb22bf2f5d953723c072a0c Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 14 Sep 2016 17:16:54 -0700 Subject: [PATCH] Add an "Export as .ics" action to Calendar events Summary: Ref T10747. Allows you to grab an event as a (basic) ICS file. Test Plan: - Exported a normal event. - Exported an all-day event. {F1830577} Reviewers: chad Reviewed By: chad Maniphest Tasks: T10747 Differential Revision: https://secure.phabricator.com/D16553 --- src/__phutil_library_map__.php | 2 + .../PhabricatorCalendarApplication.php | 2 + ...abricatorCalendarEventExportController.php | 54 +++++++++++++++++++ ...PhabricatorCalendarEventViewController.php | 9 ++++ .../storage/PhabricatorCalendarEvent.php | 37 +++++++++++++ 5 files changed, 104 insertions(+) create mode 100644 src/applications/calendar/controller/PhabricatorCalendarEventExportController.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 49a296b8a6..dafd4928e7 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2044,6 +2044,7 @@ phutil_register_library_map(array( 'PhabricatorCalendarEventEditor' => 'applications/calendar/editor/PhabricatorCalendarEventEditor.php', 'PhabricatorCalendarEventEmailCommand' => 'applications/calendar/command/PhabricatorCalendarEventEmailCommand.php', 'PhabricatorCalendarEventEndDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventEndDateTransaction.php', + 'PhabricatorCalendarEventExportController' => 'applications/calendar/controller/PhabricatorCalendarEventExportController.php', 'PhabricatorCalendarEventFrequencyTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventFrequencyTransaction.php', 'PhabricatorCalendarEventFulltextEngine' => 'applications/calendar/search/PhabricatorCalendarEventFulltextEngine.php', 'PhabricatorCalendarEventHeraldAdapter' => 'applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php', @@ -6781,6 +6782,7 @@ phutil_register_library_map(array( 'PhabricatorCalendarEventEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorCalendarEventEmailCommand' => 'MetaMTAEmailTransactionCommand', 'PhabricatorCalendarEventEndDateTransaction' => 'PhabricatorCalendarEventDateTransaction', + 'PhabricatorCalendarEventExportController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventFrequencyTransaction' => 'PhabricatorCalendarEventTransactionType', 'PhabricatorCalendarEventFulltextEngine' => 'PhabricatorFulltextEngine', 'PhabricatorCalendarEventHeraldAdapter' => 'HeraldAdapter', diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php index 8dd68c42e0..ec3d256ece 100644 --- a/src/applications/calendar/application/PhabricatorCalendarApplication.php +++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php @@ -59,6 +59,8 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication { => 'PhabricatorCalendarEventCancelController', '(?Pjoin|decline|accept)/(?P[1-9]\d*)/' => 'PhabricatorCalendarEventJoinController', + 'export/(?P[1-9]\d*)/' + => 'PhabricatorCalendarEventExportController', ), ), ); diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventExportController.php b/src/applications/calendar/controller/PhabricatorCalendarEventExportController.php new file mode 100644 index 0000000000..e09237d627 --- /dev/null +++ b/src/applications/calendar/controller/PhabricatorCalendarEventExportController.php @@ -0,0 +1,54 @@ +getViewer(); + $id = $request->getURIData('id'); + + $event = id(new PhabricatorCalendarEventQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$event) { + return new Aphront404Response(); + } + + if ($request->isFormPost()) { + $file_name = $event->getMonogram().'.ics'; + + $event_node = $event->newIntermediateEventNode(); + + $document_node = id(new PhutilCalendarDocumentNode()) + ->appendChild($event_node); + + $root_node = id(new PhutilCalendarRootNode()) + ->appendChild($document_node); + + $ics_data = id(new PhutilICSWriter()) + ->writeICSDocument($root_node); + + return id(new AphrontFileResponse()) + ->setDownload($file_name) + ->setMimeType('text/calendar') + ->setContent($ics_data); + } + + return $this->newDialog() + ->setDisableWorkflowOnSubmit(true) + ->setTitle(pht('Export as .ics')) + ->appendParagraph( + pht( + 'WARNING: This feature is a prototype and only supports a limited '. + 'set of features. Keep your expectations low!')) + ->addSubmitButton(pht('Download .ics')) + ->addCancelButton($event->getURI(), pht('Close')); + + } + +} diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php index 4c794ff496..4a0e6e7650 100644 --- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php @@ -193,6 +193,15 @@ final class PhabricatorCalendarEventViewController ->setWorkflow(true)); } + $export_uri = $this->getApplicationURI("event/export/{$id}/"); + + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Export as .ics')) + ->setIcon('fa-download') + ->setHref($export_uri) + ->setWorkflow(true)); + return $curtain; } diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php index 704ccb5c92..93ca779794 100644 --- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php +++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php @@ -626,6 +626,43 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO } + public function newIntermediateEventNode() { + $base_uri = new PhutilURI(PhabricatorEnv::getProductionURI('/')); + $domain = $base_uri->getDomain(); + + $uid = $this->getPHID().'@'.$domain; + + $created = $this->getDateCreated(); + $created = PhutilCalendarAbsoluteDateTime::newFromEpoch($created); + + $modified = $this->getDateModified(); + $modified = PhutilCalendarAbsoluteDateTime::newFromEpoch($modified); + + $date_start = $this->getDateFrom(); + $date_start = PhutilCalendarAbsoluteDateTime::newFromEpoch($date_start); + + $date_end = $this->getDateTo(); + $date_end = PhutilCalendarAbsoluteDateTime::newFromEpoch($date_end); + + if ($this->getIsAllDay()) { + $date_start->setIsAllDay(true); + $date_end->setIsAllDay(true); + } + + $node = id(new PhutilCalendarEventNode()) + ->setUID($uid) + ->setName($this->getName()) + ->setDescription($this->getDescription()) + ->setCreatedDateTime($created) + ->setModifiedDateTime($modified) + ->setStartDateTime($date_start) + ->setEndDateTime($date_end); + + return $node; + } + + + /* -( Markup Interface )--------------------------------------------------- */