1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 11:30:55 +01:00

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
This commit is contained in:
epriestley 2016-10-19 09:29:10 -07:00
parent c3de8f8305
commit 314dc30017
8 changed files with 332 additions and 94 deletions

View file

@ -2100,7 +2100,9 @@ phutil_register_library_map(array(
'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 @@ phutil_register_library_map(array(
'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 @@ phutil_register_library_map(array(
'PhabricatorCalendarExportViewController' => 'PhabricatorCalendarController',
'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO',
'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase',
'PhabricatorCalendarICSFileImportEngine' => 'PhabricatorCalendarICSImportEngine',
'PhabricatorCalendarICSImportEngine' => 'PhabricatorCalendarImportEngine',
'PhabricatorCalendarICSURIImportEngine' => 'PhabricatorCalendarICSImportEngine',
'PhabricatorCalendarICSWriter' => 'Phobject',
'PhabricatorCalendarIconSet' => 'PhabricatorIconSet',
'PhabricatorCalendarImport' => array(
@ -6953,9 +6959,11 @@ phutil_register_library_map(array(
'PhabricatorCalendarImportEmptyLogType' => 'PhabricatorCalendarImportLogType',
'PhabricatorCalendarImportEngine' => 'Phobject',
'PhabricatorCalendarImportEpochLogType' => 'PhabricatorCalendarImportLogType',
'PhabricatorCalendarImportFetchLogType' => 'PhabricatorCalendarImportLogType',
'PhabricatorCalendarImportFrequencyLogType' => 'PhabricatorCalendarImportLogType',
'PhabricatorCalendarImportICSFileTransaction' => 'PhabricatorCalendarImportTransactionType',
'PhabricatorCalendarImportICSLogType' => 'PhabricatorCalendarImportLogType',
'PhabricatorCalendarImportICSURITransaction' => 'PhabricatorCalendarImportTransactionType',
'PhabricatorCalendarImportIgnoredNodeLogType' => 'PhabricatorCalendarImportLogType',
'PhabricatorCalendarImportListController' => 'PhabricatorCalendarController',
'PhabricatorCalendarImportLog' => array(

View file

@ -0,0 +1,102 @@
<?php
final class PhabricatorCalendarICSFileImportEngine
extends PhabricatorCalendarICSImportEngine {
const ENGINETYPE = 'icsfile';
public function getImportEngineName() {
return pht('Import .ics File');
}
public function getImportEngineTypeName() {
return pht('.ics File');
}
public function getImportEngineHint() {
return pht('Import an event in ".ics" (iCalendar) format.');
}
public function appendImportProperties(
PhabricatorUser $viewer,
PhabricatorCalendarImport $import,
PHUIPropertyListView $properties) {
$phid_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_FILE;
$file_phid = $import->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();
return $this->importICSData($viewer, $import, $data);
}
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.');
}
}

View file

@ -1,84 +1,12 @@
<?php
final class PhabricatorCalendarICSImportEngine
abstract class PhabricatorCalendarICSImportEngine
extends PhabricatorCalendarImportEngine {
const ENGINETYPE = 'ics';
public function getImportEngineName() {
return pht('Import .ics File');
}
public function getImportEngineTypeName() {
return pht('.ics File');
}
public function getImportEngineHint() {
return pht('Import an event in ".ics" (iCalendar) format.');
}
public function appendImportProperties(
final protected function importICSData(
PhabricatorUser $viewer,
PhabricatorCalendarImport $import,
PHUIPropertyListView $properties) {
$phid_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_FILE;
$file_phid = $import->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 @@ final class PhabricatorCalendarICSImportEngine
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.');
}
}

View file

@ -0,0 +1,111 @@
<?php
final class PhabricatorCalendarICSURIImportEngine
extends PhabricatorCalendarICSImportEngine {
const ENGINETYPE = 'icsuri';
public function getImportEngineName() {
return pht('Import .ics URI');
}
public function getImportEngineTypeName() {
return pht('.ics URI');
}
public function getImportEngineHint() {
return pht('Import or subscribe to a calendar in .ics format by URI.');
}
public function appendImportProperties(
PhabricatorUser $viewer,
PhabricatorCalendarImport $import,
PHUIPropertyListView $properties) {
$uri_key = PhabricatorCalendarImportICSURITransaction::PARAMKEY_URI;
$uri = $import->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;
}
}

View file

@ -0,0 +1,33 @@
<?php
final class PhabricatorCalendarImportFetchLogType
extends PhabricatorCalendarImportLogType {
const LOGTYPE = 'fetch';
public function getDisplayType(
PhabricatorUser $viewer,
PhabricatorCalendarImportLog $log) {
return pht('Fetched Calendar');
}
public function getDisplayDescription(
PhabricatorUser $viewer,
PhabricatorCalendarImportLog $log) {
return $viewer->renderHandle($log->getParameter('file.phid'));
}
public function getDisplayIcon(
PhabricatorUser $viewer,
PhabricatorCalendarImportLog $log) {
return 'fa-download';
}
public function getDisplayColor(
PhabricatorUser $viewer,
PhabricatorCalendarImportLog $log) {
return 'green';
}
}

View file

@ -15,7 +15,7 @@ final class PhabricatorCalendarImportICSLogType
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'));
}

View file

@ -34,7 +34,7 @@ final class PhabricatorCalendarImportICSFileTransaction
$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) {

View file

@ -0,0 +1,73 @@
<?php
final class PhabricatorCalendarImportICSURITransaction
extends PhabricatorCalendarImportTransactionType {
const TRANSACTIONTYPE = 'calendar.import.ics.uri';
const PARAMKEY_URI = 'ics.uri';
public function generateOldValue($object) {
return $object->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;
}
}