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 @@ -2100,7 +2100,9 @@ 'PhabricatorCalendarExportViewController' => 'applications/calendar/controller/PhabricatorCalendarExportViewController.php', 'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php', 'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php', + 'PhabricatorCalendarICSFileImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php', 'PhabricatorCalendarICSImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSImportEngine.php', + 'PhabricatorCalendarICSURIImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php', 'PhabricatorCalendarICSWriter' => 'applications/calendar/util/PhabricatorCalendarICSWriter.php', 'PhabricatorCalendarIconSet' => 'applications/calendar/icon/PhabricatorCalendarIconSet.php', 'PhabricatorCalendarImport' => 'applications/calendar/storage/PhabricatorCalendarImport.php', @@ -2117,9 +2119,11 @@ 'PhabricatorCalendarImportEmptyLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportEmptyLogType.php', 'PhabricatorCalendarImportEngine' => 'applications/calendar/import/PhabricatorCalendarImportEngine.php', 'PhabricatorCalendarImportEpochLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportEpochLogType.php', + 'PhabricatorCalendarImportFetchLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportFetchLogType.php', 'PhabricatorCalendarImportFrequencyLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportFrequencyLogType.php', 'PhabricatorCalendarImportICSFileTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php', 'PhabricatorCalendarImportICSLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportICSLogType.php', + 'PhabricatorCalendarImportICSURITransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportICSURITransaction.php', 'PhabricatorCalendarImportIgnoredNodeLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportIgnoredNodeLogType.php', 'PhabricatorCalendarImportListController' => 'applications/calendar/controller/PhabricatorCalendarImportListController.php', 'PhabricatorCalendarImportLog' => 'applications/calendar/storage/PhabricatorCalendarImportLog.php', @@ -6931,7 +6935,9 @@ 'PhabricatorCalendarExportViewController' => 'PhabricatorCalendarController', 'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO', 'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase', + 'PhabricatorCalendarICSFileImportEngine' => 'PhabricatorCalendarICSImportEngine', 'PhabricatorCalendarICSImportEngine' => 'PhabricatorCalendarImportEngine', + 'PhabricatorCalendarICSURIImportEngine' => 'PhabricatorCalendarICSImportEngine', 'PhabricatorCalendarICSWriter' => 'Phobject', 'PhabricatorCalendarIconSet' => 'PhabricatorIconSet', 'PhabricatorCalendarImport' => array( @@ -6953,9 +6959,11 @@ 'PhabricatorCalendarImportEmptyLogType' => 'PhabricatorCalendarImportLogType', 'PhabricatorCalendarImportEngine' => 'Phobject', 'PhabricatorCalendarImportEpochLogType' => 'PhabricatorCalendarImportLogType', + 'PhabricatorCalendarImportFetchLogType' => 'PhabricatorCalendarImportLogType', 'PhabricatorCalendarImportFrequencyLogType' => 'PhabricatorCalendarImportLogType', 'PhabricatorCalendarImportICSFileTransaction' => 'PhabricatorCalendarImportTransactionType', 'PhabricatorCalendarImportICSLogType' => 'PhabricatorCalendarImportLogType', + 'PhabricatorCalendarImportICSURITransaction' => 'PhabricatorCalendarImportTransactionType', 'PhabricatorCalendarImportIgnoredNodeLogType' => 'PhabricatorCalendarImportLogType', 'PhabricatorCalendarImportListController' => 'PhabricatorCalendarController', 'PhabricatorCalendarImportLog' => array( diff --git a/src/applications/calendar/import/PhabricatorCalendarICSImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php copy from src/applications/calendar/import/PhabricatorCalendarICSImportEngine.php copy to src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php --- a/src/applications/calendar/import/PhabricatorCalendarICSImportEngine.php +++ b/src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php @@ -1,9 +1,9 @@ loadFileData(); - $parser = new PhutilICSParser(); - - try { - $document = $parser->parseICSData($data); - } catch (PhutilICSParserException $ex) { - // TODO: In theory, it would be nice to store these in a fully abstract - // form so they can be translated at display time. As-is, we'll store the - // error messages in whatever language we were using when the parser - // failure occurred. - - $import->newLogMessage( - PhabricatorCalendarImportICSLogType::LOGTYPE, - array( - 'ics.code' => $ex->getParserFailureCode(), - 'ics.message' => $ex->getMessage(), - )); - - $document = null; - } - - return $this->importEventDocument($viewer, $import, $document); + return $this->importICSData($viewer, $import, $data); } diff --git a/src/applications/calendar/import/PhabricatorCalendarICSImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarICSImportEngine.php --- a/src/applications/calendar/import/PhabricatorCalendarICSImportEngine.php +++ b/src/applications/calendar/import/PhabricatorCalendarICSImportEngine.php @@ -1,84 +1,12 @@ getParameter($phid_key); - - $properties->addProperty( - pht('Source File'), - $viewer->renderHandle($file_phid)); - } - - public function newEditEngineFields( - PhabricatorEditEngine $engine, - PhabricatorCalendarImport $import) { - $fields = array(); - - if ($engine->getIsCreate()) { - $fields[] = id(new PhabricatorFileEditField()) - ->setKey('icsFilePHID') - ->setLabel(pht('ICS File')) - ->setDescription(pht('ICS file to import.')) - ->setTransactionType( - PhabricatorCalendarImportICSFileTransaction::TRANSACTIONTYPE) - ->setConduitDescription(pht('File PHID to import.')) - ->setConduitTypeDescription(pht('File PHID.')); - } - - return $fields; - } - - public function getDisplayName(PhabricatorCalendarImport $import) { - $filename_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_NAME; - $filename = $import->getParameter($filename_key); - if (strlen($filename)) { - return pht('ICS File "%s"', $filename); - } else { - return pht('ICS File'); - } - } - - public function didCreateImport( - PhabricatorUser $viewer, - PhabricatorCalendarImport $import) { - - $phid_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_FILE; - $file_phid = $import->getParameter($phid_key); - - $file = id(new PhabricatorFileQuery()) - ->setViewer($viewer) - ->withPHIDs(array($file_phid)) - ->executeOne(); - if (!$file) { - throw new Exception( - pht( - 'Unable to load file ("%s") for import.', - $file_phid)); - } - - $data = $file->loadFileData(); + $data) { $parser = new PhutilICSParser(); @@ -103,21 +31,4 @@ return $this->importEventDocument($viewer, $import, $document); } - - public function canDisable( - PhabricatorUser $viewer, - PhabricatorCalendarImport $import) { - return false; - } - - public function explainCanDisable( - PhabricatorUser $viewer, - PhabricatorCalendarImport $import) { - return pht( - 'You can not disable import of an ICS file because the entire import '. - 'occurs immediately when you upload the file. There is no further '. - 'activity to disable.'); - } - - } diff --git a/src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php @@ -0,0 +1,111 @@ +getParameter($uri_key); + + // Since the URI may contain a secret hash, don't show it to users who + // can not edit the import. + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $import, + PhabricatorPolicyCapability::CAN_EDIT); + if (!$can_edit) { + $uri_display = phutil_tag('em', array(), pht('Restricted')); + } else if (!PhabricatorEnv::isValidRemoteURIForLink($uri)) { + $uri_display = $uri; + } else { + $uri_display = phutil_tag( + 'a', + array( + 'href' => $uri, + 'target' => '_blank', + ), + $uri); + } + + $properties->addProperty(pht('Source URI'), $uri_display); + } + + public function newEditEngineFields( + PhabricatorEditEngine $engine, + PhabricatorCalendarImport $import) { + $fields = array(); + + if ($engine->getIsCreate()) { + $fields[] = id(new PhabricatorTextEditField()) + ->setKey('uri') + ->setLabel(pht('URI')) + ->setDescription(pht('URI to import.')) + ->setTransactionType( + PhabricatorCalendarImportICSURITransaction::TRANSACTIONTYPE) + ->setConduitDescription(pht('URI to import.')) + ->setConduitTypeDescription(pht('New URI.')); + } + + return $fields; + } + + public function getDisplayName(PhabricatorCalendarImport $import) { + return pht('ICS URI'); + } + + public function didCreateImport( + PhabricatorUser $viewer, + PhabricatorCalendarImport $import) { + + $uri_key = PhabricatorCalendarImportICSURITransaction::PARAMKEY_URI; + $uri = $import->getParameter($uri_key); + + PhabricatorSystemActionEngine::willTakeAction( + array($viewer->getPHID()), + new PhabricatorFilesOutboundRequestAction(), + 1); + + $file = PhabricatorFile::newFromFileDownload( + $uri, + array( + 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, + 'authorPHID' => $import->getAuthorPHID(), + 'canCDN' => true, + )); + + $import->newLogMessage( + PhabricatorCalendarImportFetchLogType::LOGTYPE, + array( + 'file.phid' => $file->getPHID(), + )); + + $data = $file->loadFileData(); + + return $this->importICSData($viewer, $import, $data); + } + + public function canDisable( + PhabricatorUser $viewer, + PhabricatorCalendarImport $import) { + return true; + } + +} diff --git a/src/applications/calendar/importlog/PhabricatorCalendarImportICSLogType.php b/src/applications/calendar/importlog/PhabricatorCalendarImportFetchLogType.php copy from src/applications/calendar/importlog/PhabricatorCalendarImportICSLogType.php copy to src/applications/calendar/importlog/PhabricatorCalendarImportFetchLogType.php --- a/src/applications/calendar/importlog/PhabricatorCalendarImportICSLogType.php +++ b/src/applications/calendar/importlog/PhabricatorCalendarImportFetchLogType.php @@ -1,36 +1,33 @@ getParameter('ics.code'), - $log->getParameter('ics.message')); - } + return $viewer->renderHandle($log->getParameter('file.phid')); + } public function getDisplayIcon( PhabricatorUser $viewer, PhabricatorCalendarImportLog $log) { - return 'fa-file'; + return 'fa-download'; } public function getDisplayColor( PhabricatorUser $viewer, PhabricatorCalendarImportLog $log) { - return 'red'; + return 'green'; } } diff --git a/src/applications/calendar/importlog/PhabricatorCalendarImportICSLogType.php b/src/applications/calendar/importlog/PhabricatorCalendarImportICSLogType.php --- a/src/applications/calendar/importlog/PhabricatorCalendarImportICSLogType.php +++ b/src/applications/calendar/importlog/PhabricatorCalendarImportICSLogType.php @@ -15,7 +15,7 @@ PhabricatorUser $viewer, PhabricatorCalendarImportLog $log) { return pht( - 'Failed to parse ICS file ("%s"): %s', + 'Failed to parse ICS data ("%s"): %s', $log->getParameter('ics.code'), $log->getParameter('ics.message')); } diff --git a/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php --- a/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php +++ b/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php @@ -34,7 +34,7 @@ $viewer = $this->getActor(); $errors = array(); - $ics_type = PhabricatorCalendarICSImportEngine::ENGINETYPE; + $ics_type = PhabricatorCalendarICSFileImportEngine::ENGINETYPE; $import_type = $object->getEngine()->getImportEngineType(); if ($import_type != $ics_type) { if (!$xactions) { diff --git a/src/applications/calendar/xaction/PhabricatorCalendarImportICSURITransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarImportICSURITransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/xaction/PhabricatorCalendarImportICSURITransaction.php @@ -0,0 +1,73 @@ +getParameter(self::PARAMKEY_URI); + } + + public function applyInternalEffects($object, $value) { + $object->setParameter(self::PARAMKEY_URI, $value); + } + + public function getTitle() { + // NOTE: This transaction intentionally does not disclose the actual + // URI. + return pht( + '%s updated the import URI.', + $this->renderAuthor()); + } + + public function validateTransactions($object, array $xactions) { + $viewer = $this->getActor(); + $errors = array(); + + $ics_type = PhabricatorCalendarICSURIImportEngine::ENGINETYPE; + $import_type = $object->getEngine()->getImportEngineType(); + if ($import_type != $ics_type) { + if (!$xactions) { + return $errors; + } + + $errors[] = $this->newInvalidError( + pht( + 'You can not attach an ICS URI to an import type other than '. + 'an ICS URI import (type is "%s").', + $import_type)); + + return $errors; + } + + $new_value = $object->getParameter(self::PARAMKEY_URI); + foreach ($xactions as $xaction) { + $new_value = $xaction->getNewValue(); + if (!strlen($new_value)) { + continue; + } + + try { + PhabricatorEnv::requireValidRemoteURIForFetch( + $new_value, + array( + 'http', + 'https', + )); + } catch (Exception $ex) { + $errors[] = $this->newInvalidError( + $ex->getMessage(), + $xaction); + } + } + + if (!strlen($new_value)) { + $errors[] = $this->newRequiredError( + pht('You must select an ".ics" URI to import.')); + } + + return $errors; + } +}