@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add a URI-based ICS import source engine

Summary: Ref T10747. This doesn't have a "keep up to date" option yet, but can, e.g., fetch a Google Calendar URI

Test Plan: Fetched a Google Calendar URI, got some events imported.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10747

Differential Revision: https://secure.phabricator.com/D16730

+332 -94
+8
src/__phutil_library_map__.php
··· 2100 2100 'PhabricatorCalendarExportViewController' => 'applications/calendar/controller/PhabricatorCalendarExportViewController.php', 2101 2101 'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php', 2102 2102 'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php', 2103 + 'PhabricatorCalendarICSFileImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php', 2103 2104 'PhabricatorCalendarICSImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSImportEngine.php', 2105 + 'PhabricatorCalendarICSURIImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php', 2104 2106 'PhabricatorCalendarICSWriter' => 'applications/calendar/util/PhabricatorCalendarICSWriter.php', 2105 2107 'PhabricatorCalendarIconSet' => 'applications/calendar/icon/PhabricatorCalendarIconSet.php', 2106 2108 'PhabricatorCalendarImport' => 'applications/calendar/storage/PhabricatorCalendarImport.php', ··· 2117 2119 'PhabricatorCalendarImportEmptyLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportEmptyLogType.php', 2118 2120 'PhabricatorCalendarImportEngine' => 'applications/calendar/import/PhabricatorCalendarImportEngine.php', 2119 2121 'PhabricatorCalendarImportEpochLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportEpochLogType.php', 2122 + 'PhabricatorCalendarImportFetchLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportFetchLogType.php', 2120 2123 'PhabricatorCalendarImportFrequencyLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportFrequencyLogType.php', 2121 2124 'PhabricatorCalendarImportICSFileTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php', 2122 2125 'PhabricatorCalendarImportICSLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportICSLogType.php', 2126 + 'PhabricatorCalendarImportICSURITransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportICSURITransaction.php', 2123 2127 'PhabricatorCalendarImportIgnoredNodeLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportIgnoredNodeLogType.php', 2124 2128 'PhabricatorCalendarImportListController' => 'applications/calendar/controller/PhabricatorCalendarImportListController.php', 2125 2129 'PhabricatorCalendarImportLog' => 'applications/calendar/storage/PhabricatorCalendarImportLog.php', ··· 6931 6935 'PhabricatorCalendarExportViewController' => 'PhabricatorCalendarController', 6932 6936 'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO', 6933 6937 'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase', 6938 + 'PhabricatorCalendarICSFileImportEngine' => 'PhabricatorCalendarICSImportEngine', 6934 6939 'PhabricatorCalendarICSImportEngine' => 'PhabricatorCalendarImportEngine', 6940 + 'PhabricatorCalendarICSURIImportEngine' => 'PhabricatorCalendarICSImportEngine', 6935 6941 'PhabricatorCalendarICSWriter' => 'Phobject', 6936 6942 'PhabricatorCalendarIconSet' => 'PhabricatorIconSet', 6937 6943 'PhabricatorCalendarImport' => array( ··· 6953 6959 'PhabricatorCalendarImportEmptyLogType' => 'PhabricatorCalendarImportLogType', 6954 6960 'PhabricatorCalendarImportEngine' => 'Phobject', 6955 6961 'PhabricatorCalendarImportEpochLogType' => 'PhabricatorCalendarImportLogType', 6962 + 'PhabricatorCalendarImportFetchLogType' => 'PhabricatorCalendarImportLogType', 6956 6963 'PhabricatorCalendarImportFrequencyLogType' => 'PhabricatorCalendarImportLogType', 6957 6964 'PhabricatorCalendarImportICSFileTransaction' => 'PhabricatorCalendarImportTransactionType', 6958 6965 'PhabricatorCalendarImportICSLogType' => 'PhabricatorCalendarImportLogType', 6966 + 'PhabricatorCalendarImportICSURITransaction' => 'PhabricatorCalendarImportTransactionType', 6959 6967 'PhabricatorCalendarImportIgnoredNodeLogType' => 'PhabricatorCalendarImportLogType', 6960 6968 'PhabricatorCalendarImportListController' => 'PhabricatorCalendarController', 6961 6969 'PhabricatorCalendarImportLog' => array(
+102
src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorCalendarICSFileImportEngine 4 + extends PhabricatorCalendarICSImportEngine { 5 + 6 + const ENGINETYPE = 'icsfile'; 7 + 8 + public function getImportEngineName() { 9 + return pht('Import .ics File'); 10 + } 11 + 12 + public function getImportEngineTypeName() { 13 + return pht('.ics File'); 14 + } 15 + 16 + public function getImportEngineHint() { 17 + return pht('Import an event in ".ics" (iCalendar) format.'); 18 + } 19 + 20 + public function appendImportProperties( 21 + PhabricatorUser $viewer, 22 + PhabricatorCalendarImport $import, 23 + PHUIPropertyListView $properties) { 24 + 25 + $phid_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_FILE; 26 + $file_phid = $import->getParameter($phid_key); 27 + 28 + $properties->addProperty( 29 + pht('Source File'), 30 + $viewer->renderHandle($file_phid)); 31 + } 32 + 33 + public function newEditEngineFields( 34 + PhabricatorEditEngine $engine, 35 + PhabricatorCalendarImport $import) { 36 + $fields = array(); 37 + 38 + if ($engine->getIsCreate()) { 39 + $fields[] = id(new PhabricatorFileEditField()) 40 + ->setKey('icsFilePHID') 41 + ->setLabel(pht('ICS File')) 42 + ->setDescription(pht('ICS file to import.')) 43 + ->setTransactionType( 44 + PhabricatorCalendarImportICSFileTransaction::TRANSACTIONTYPE) 45 + ->setConduitDescription(pht('File PHID to import.')) 46 + ->setConduitTypeDescription(pht('File PHID.')); 47 + } 48 + 49 + return $fields; 50 + } 51 + 52 + public function getDisplayName(PhabricatorCalendarImport $import) { 53 + $filename_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_NAME; 54 + $filename = $import->getParameter($filename_key); 55 + if (strlen($filename)) { 56 + return pht('ICS File "%s"', $filename); 57 + } else { 58 + return pht('ICS File'); 59 + } 60 + } 61 + 62 + public function didCreateImport( 63 + PhabricatorUser $viewer, 64 + PhabricatorCalendarImport $import) { 65 + 66 + $phid_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_FILE; 67 + $file_phid = $import->getParameter($phid_key); 68 + 69 + $file = id(new PhabricatorFileQuery()) 70 + ->setViewer($viewer) 71 + ->withPHIDs(array($file_phid)) 72 + ->executeOne(); 73 + if (!$file) { 74 + throw new Exception( 75 + pht( 76 + 'Unable to load file ("%s") for import.', 77 + $file_phid)); 78 + } 79 + 80 + $data = $file->loadFileData(); 81 + 82 + return $this->importICSData($viewer, $import, $data); 83 + } 84 + 85 + 86 + public function canDisable( 87 + PhabricatorUser $viewer, 88 + PhabricatorCalendarImport $import) { 89 + return false; 90 + } 91 + 92 + public function explainCanDisable( 93 + PhabricatorUser $viewer, 94 + PhabricatorCalendarImport $import) { 95 + return pht( 96 + 'You can not disable import of an ICS file because the entire import '. 97 + 'occurs immediately when you upload the file. There is no further '. 98 + 'activity to disable.'); 99 + } 100 + 101 + 102 + }
+3 -92
src/applications/calendar/import/PhabricatorCalendarICSImportEngine.php
··· 1 1 <?php 2 2 3 - final class PhabricatorCalendarICSImportEngine 3 + abstract class PhabricatorCalendarICSImportEngine 4 4 extends PhabricatorCalendarImportEngine { 5 5 6 - const ENGINETYPE = 'ics'; 7 - 8 - public function getImportEngineName() { 9 - return pht('Import .ics File'); 10 - } 11 - 12 - public function getImportEngineTypeName() { 13 - return pht('.ics File'); 14 - } 15 - 16 - public function getImportEngineHint() { 17 - return pht('Import an event in ".ics" (iCalendar) format.'); 18 - } 19 - 20 - 21 - public function appendImportProperties( 6 + final protected function importICSData( 22 7 PhabricatorUser $viewer, 23 8 PhabricatorCalendarImport $import, 24 - PHUIPropertyListView $properties) { 25 - 26 - $phid_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_FILE; 27 - $file_phid = $import->getParameter($phid_key); 28 - 29 - $properties->addProperty( 30 - pht('Source File'), 31 - $viewer->renderHandle($file_phid)); 32 - } 33 - 34 - public function newEditEngineFields( 35 - PhabricatorEditEngine $engine, 36 - PhabricatorCalendarImport $import) { 37 - $fields = array(); 38 - 39 - if ($engine->getIsCreate()) { 40 - $fields[] = id(new PhabricatorFileEditField()) 41 - ->setKey('icsFilePHID') 42 - ->setLabel(pht('ICS File')) 43 - ->setDescription(pht('ICS file to import.')) 44 - ->setTransactionType( 45 - PhabricatorCalendarImportICSFileTransaction::TRANSACTIONTYPE) 46 - ->setConduitDescription(pht('File PHID to import.')) 47 - ->setConduitTypeDescription(pht('File PHID.')); 48 - } 49 - 50 - return $fields; 51 - } 52 - 53 - public function getDisplayName(PhabricatorCalendarImport $import) { 54 - $filename_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_NAME; 55 - $filename = $import->getParameter($filename_key); 56 - if (strlen($filename)) { 57 - return pht('ICS File "%s"', $filename); 58 - } else { 59 - return pht('ICS File'); 60 - } 61 - } 62 - 63 - public function didCreateImport( 64 - PhabricatorUser $viewer, 65 - PhabricatorCalendarImport $import) { 66 - 67 - $phid_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_FILE; 68 - $file_phid = $import->getParameter($phid_key); 69 - 70 - $file = id(new PhabricatorFileQuery()) 71 - ->setViewer($viewer) 72 - ->withPHIDs(array($file_phid)) 73 - ->executeOne(); 74 - if (!$file) { 75 - throw new Exception( 76 - pht( 77 - 'Unable to load file ("%s") for import.', 78 - $file_phid)); 79 - } 80 - 81 - $data = $file->loadFileData(); 9 + $data) { 82 10 83 11 $parser = new PhutilICSParser(); 84 12 ··· 102 30 103 31 return $this->importEventDocument($viewer, $import, $document); 104 32 } 105 - 106 - 107 - public function canDisable( 108 - PhabricatorUser $viewer, 109 - PhabricatorCalendarImport $import) { 110 - return false; 111 - } 112 - 113 - public function explainCanDisable( 114 - PhabricatorUser $viewer, 115 - PhabricatorCalendarImport $import) { 116 - return pht( 117 - 'You can not disable import of an ICS file because the entire import '. 118 - 'occurs immediately when you upload the file. There is no further '. 119 - 'activity to disable.'); 120 - } 121 - 122 33 123 34 }
+111
src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorCalendarICSURIImportEngine 4 + extends PhabricatorCalendarICSImportEngine { 5 + 6 + const ENGINETYPE = 'icsuri'; 7 + 8 + public function getImportEngineName() { 9 + return pht('Import .ics URI'); 10 + } 11 + 12 + public function getImportEngineTypeName() { 13 + return pht('.ics URI'); 14 + } 15 + 16 + public function getImportEngineHint() { 17 + return pht('Import or subscribe to a calendar in .ics format by URI.'); 18 + } 19 + 20 + public function appendImportProperties( 21 + PhabricatorUser $viewer, 22 + PhabricatorCalendarImport $import, 23 + PHUIPropertyListView $properties) { 24 + 25 + $uri_key = PhabricatorCalendarImportICSURITransaction::PARAMKEY_URI; 26 + $uri = $import->getParameter($uri_key); 27 + 28 + // Since the URI may contain a secret hash, don't show it to users who 29 + // can not edit the import. 30 + $can_edit = PhabricatorPolicyFilter::hasCapability( 31 + $viewer, 32 + $import, 33 + PhabricatorPolicyCapability::CAN_EDIT); 34 + if (!$can_edit) { 35 + $uri_display = phutil_tag('em', array(), pht('Restricted')); 36 + } else if (!PhabricatorEnv::isValidRemoteURIForLink($uri)) { 37 + $uri_display = $uri; 38 + } else { 39 + $uri_display = phutil_tag( 40 + 'a', 41 + array( 42 + 'href' => $uri, 43 + 'target' => '_blank', 44 + ), 45 + $uri); 46 + } 47 + 48 + $properties->addProperty(pht('Source URI'), $uri_display); 49 + } 50 + 51 + public function newEditEngineFields( 52 + PhabricatorEditEngine $engine, 53 + PhabricatorCalendarImport $import) { 54 + $fields = array(); 55 + 56 + if ($engine->getIsCreate()) { 57 + $fields[] = id(new PhabricatorTextEditField()) 58 + ->setKey('uri') 59 + ->setLabel(pht('URI')) 60 + ->setDescription(pht('URI to import.')) 61 + ->setTransactionType( 62 + PhabricatorCalendarImportICSURITransaction::TRANSACTIONTYPE) 63 + ->setConduitDescription(pht('URI to import.')) 64 + ->setConduitTypeDescription(pht('New URI.')); 65 + } 66 + 67 + return $fields; 68 + } 69 + 70 + public function getDisplayName(PhabricatorCalendarImport $import) { 71 + return pht('ICS URI'); 72 + } 73 + 74 + public function didCreateImport( 75 + PhabricatorUser $viewer, 76 + PhabricatorCalendarImport $import) { 77 + 78 + $uri_key = PhabricatorCalendarImportICSURITransaction::PARAMKEY_URI; 79 + $uri = $import->getParameter($uri_key); 80 + 81 + PhabricatorSystemActionEngine::willTakeAction( 82 + array($viewer->getPHID()), 83 + new PhabricatorFilesOutboundRequestAction(), 84 + 1); 85 + 86 + $file = PhabricatorFile::newFromFileDownload( 87 + $uri, 88 + array( 89 + 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, 90 + 'authorPHID' => $import->getAuthorPHID(), 91 + 'canCDN' => true, 92 + )); 93 + 94 + $import->newLogMessage( 95 + PhabricatorCalendarImportFetchLogType::LOGTYPE, 96 + array( 97 + 'file.phid' => $file->getPHID(), 98 + )); 99 + 100 + $data = $file->loadFileData(); 101 + 102 + return $this->importICSData($viewer, $import, $data); 103 + } 104 + 105 + public function canDisable( 106 + PhabricatorUser $viewer, 107 + PhabricatorCalendarImport $import) { 108 + return true; 109 + } 110 + 111 + }
+33
src/applications/calendar/importlog/PhabricatorCalendarImportFetchLogType.php
··· 1 + <?php 2 + 3 + final class PhabricatorCalendarImportFetchLogType 4 + extends PhabricatorCalendarImportLogType { 5 + 6 + const LOGTYPE = 'fetch'; 7 + 8 + public function getDisplayType( 9 + PhabricatorUser $viewer, 10 + PhabricatorCalendarImportLog $log) { 11 + return pht('Fetched Calendar'); 12 + } 13 + 14 + public function getDisplayDescription( 15 + PhabricatorUser $viewer, 16 + PhabricatorCalendarImportLog $log) { 17 + 18 + return $viewer->renderHandle($log->getParameter('file.phid')); 19 + } 20 + 21 + public function getDisplayIcon( 22 + PhabricatorUser $viewer, 23 + PhabricatorCalendarImportLog $log) { 24 + return 'fa-download'; 25 + } 26 + 27 + public function getDisplayColor( 28 + PhabricatorUser $viewer, 29 + PhabricatorCalendarImportLog $log) { 30 + return 'green'; 31 + } 32 + 33 + }
+1 -1
src/applications/calendar/importlog/PhabricatorCalendarImportICSLogType.php
··· 15 15 PhabricatorUser $viewer, 16 16 PhabricatorCalendarImportLog $log) { 17 17 return pht( 18 - 'Failed to parse ICS file ("%s"): %s', 18 + 'Failed to parse ICS data ("%s"): %s', 19 19 $log->getParameter('ics.code'), 20 20 $log->getParameter('ics.message')); 21 21 }
+1 -1
src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php
··· 34 34 $viewer = $this->getActor(); 35 35 $errors = array(); 36 36 37 - $ics_type = PhabricatorCalendarICSImportEngine::ENGINETYPE; 37 + $ics_type = PhabricatorCalendarICSFileImportEngine::ENGINETYPE; 38 38 $import_type = $object->getEngine()->getImportEngineType(); 39 39 if ($import_type != $ics_type) { 40 40 if (!$xactions) {
+73
src/applications/calendar/xaction/PhabricatorCalendarImportICSURITransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorCalendarImportICSURITransaction 4 + extends PhabricatorCalendarImportTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'calendar.import.ics.uri'; 7 + const PARAMKEY_URI = 'ics.uri'; 8 + 9 + public function generateOldValue($object) { 10 + return $object->getParameter(self::PARAMKEY_URI); 11 + } 12 + 13 + public function applyInternalEffects($object, $value) { 14 + $object->setParameter(self::PARAMKEY_URI, $value); 15 + } 16 + 17 + public function getTitle() { 18 + // NOTE: This transaction intentionally does not disclose the actual 19 + // URI. 20 + return pht( 21 + '%s updated the import URI.', 22 + $this->renderAuthor()); 23 + } 24 + 25 + public function validateTransactions($object, array $xactions) { 26 + $viewer = $this->getActor(); 27 + $errors = array(); 28 + 29 + $ics_type = PhabricatorCalendarICSURIImportEngine::ENGINETYPE; 30 + $import_type = $object->getEngine()->getImportEngineType(); 31 + if ($import_type != $ics_type) { 32 + if (!$xactions) { 33 + return $errors; 34 + } 35 + 36 + $errors[] = $this->newInvalidError( 37 + pht( 38 + 'You can not attach an ICS URI to an import type other than '. 39 + 'an ICS URI import (type is "%s").', 40 + $import_type)); 41 + 42 + return $errors; 43 + } 44 + 45 + $new_value = $object->getParameter(self::PARAMKEY_URI); 46 + foreach ($xactions as $xaction) { 47 + $new_value = $xaction->getNewValue(); 48 + if (!strlen($new_value)) { 49 + continue; 50 + } 51 + 52 + try { 53 + PhabricatorEnv::requireValidRemoteURIForFetch( 54 + $new_value, 55 + array( 56 + 'http', 57 + 'https', 58 + )); 59 + } catch (Exception $ex) { 60 + $errors[] = $this->newInvalidError( 61 + $ex->getMessage(), 62 + $xaction); 63 + } 64 + } 65 + 66 + if (!strlen($new_value)) { 67 + $errors[] = $this->newRequiredError( 68 + pht('You must select an ".ics" URI to import.')); 69 + } 70 + 71 + return $errors; 72 + } 73 + }