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:
parent
c3de8f8305
commit
314dc30017
8 changed files with 332 additions and 94 deletions
|
@ -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(
|
||||
|
|
|
@ -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.');
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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.');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
|
||||
}
|
|
@ -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'));
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue