Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13956426
D16699.id40199.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
37 KB
Referenced Files
None
Subscribers
None
D16699.id40199.diff
View Options
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
@@ -140,6 +140,7 @@
'AphrontDialogView' => 'view/AphrontDialogView.php',
'AphrontEpochHTTPParameterType' => 'aphront/httpparametertype/AphrontEpochHTTPParameterType.php',
'AphrontException' => 'aphront/exception/AphrontException.php',
+ 'AphrontFileHTTPParameterType' => 'aphront/httpparametertype/AphrontFileHTTPParameterType.php',
'AphrontFileResponse' => 'aphront/response/AphrontFileResponse.php',
'AphrontFormCheckboxControl' => 'view/form/control/AphrontFormCheckboxControl.php',
'AphrontFormControl' => 'view/form/control/AphrontFormControl.php',
@@ -2098,16 +2099,25 @@
'PhabricatorCalendarExportViewController' => 'applications/calendar/controller/PhabricatorCalendarExportViewController.php',
'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php',
'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php',
+ 'PhabricatorCalendarICSImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSImportEngine.php',
'PhabricatorCalendarICSWriter' => 'applications/calendar/util/PhabricatorCalendarICSWriter.php',
'PhabricatorCalendarIconSet' => 'applications/calendar/icon/PhabricatorCalendarIconSet.php',
'PhabricatorCalendarImport' => 'applications/calendar/storage/PhabricatorCalendarImport.php',
+ 'PhabricatorCalendarImportDisableTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportDisableTransaction.php',
+ 'PhabricatorCalendarImportEditController' => 'applications/calendar/controller/PhabricatorCalendarImportEditController.php',
+ 'PhabricatorCalendarImportEditEngine' => 'applications/calendar/editor/PhabricatorCalendarImportEditEngine.php',
'PhabricatorCalendarImportEditor' => 'applications/calendar/editor/PhabricatorCalendarImportEditor.php',
+ 'PhabricatorCalendarImportEngine' => 'applications/calendar/import/PhabricatorCalendarImportEngine.php',
+ 'PhabricatorCalendarImportICSFileTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php',
'PhabricatorCalendarImportListController' => 'applications/calendar/controller/PhabricatorCalendarImportListController.php',
+ 'PhabricatorCalendarImportNameTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportNameTransaction.php',
'PhabricatorCalendarImportPHIDType' => 'applications/calendar/phid/PhabricatorCalendarImportPHIDType.php',
'PhabricatorCalendarImportQuery' => 'applications/calendar/query/PhabricatorCalendarImportQuery.php',
'PhabricatorCalendarImportSearchEngine' => 'applications/calendar/query/PhabricatorCalendarImportSearchEngine.php',
'PhabricatorCalendarImportTransaction' => 'applications/calendar/storage/PhabricatorCalendarImportTransaction.php',
'PhabricatorCalendarImportTransactionQuery' => 'applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php',
+ 'PhabricatorCalendarImportTransactionType' => 'applications/calendar/xaction/PhabricatorCalendarImportTransactionType.php',
+ 'PhabricatorCalendarImportViewController' => 'applications/calendar/controller/PhabricatorCalendarImportViewController.php',
'PhabricatorCalendarRemarkupRule' => 'applications/calendar/remarkup/PhabricatorCalendarRemarkupRule.php',
'PhabricatorCalendarReplyHandler' => 'applications/calendar/mail/PhabricatorCalendarReplyHandler.php',
'PhabricatorCalendarSchemaSpec' => 'applications/calendar/storage/PhabricatorCalendarSchemaSpec.php',
@@ -2582,6 +2592,7 @@
'PhabricatorFileDeleteController' => 'applications/files/controller/PhabricatorFileDeleteController.php',
'PhabricatorFileDropUploadController' => 'applications/files/controller/PhabricatorFileDropUploadController.php',
'PhabricatorFileEditController' => 'applications/files/controller/PhabricatorFileEditController.php',
+ 'PhabricatorFileEditField' => 'applications/transactions/editfield/PhabricatorFileEditField.php',
'PhabricatorFileEditor' => 'applications/files/editor/PhabricatorFileEditor.php',
'PhabricatorFileExternalRequest' => 'applications/files/storage/PhabricatorFileExternalRequest.php',
'PhabricatorFileExternalRequestGarbageCollector' => 'applications/files/garbagecollector/PhabricatorFileExternalRequestGarbageCollector.php',
@@ -4638,6 +4649,7 @@
),
'AphrontEpochHTTPParameterType' => 'AphrontHTTPParameterType',
'AphrontException' => 'Exception',
+ 'AphrontFileHTTPParameterType' => 'AphrontHTTPParameterType',
'AphrontFileResponse' => 'AphrontResponse',
'AphrontFormCheckboxControl' => 'AphrontFormControl',
'AphrontFormControl' => 'AphrontView',
@@ -6886,6 +6898,7 @@
'PhabricatorCalendarExportViewController' => 'PhabricatorCalendarController',
'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO',
'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase',
+ 'PhabricatorCalendarICSImportEngine' => 'PhabricatorCalendarImportEngine',
'PhabricatorCalendarICSWriter' => 'Phobject',
'PhabricatorCalendarIconSet' => 'PhabricatorIconSet',
'PhabricatorCalendarImport' => array(
@@ -6894,13 +6907,21 @@
'PhabricatorApplicationTransactionInterface',
'PhabricatorDestructibleInterface',
),
+ 'PhabricatorCalendarImportDisableTransaction' => 'PhabricatorCalendarImportTransactionType',
+ 'PhabricatorCalendarImportEditController' => 'PhabricatorCalendarController',
+ 'PhabricatorCalendarImportEditEngine' => 'PhabricatorEditEngine',
'PhabricatorCalendarImportEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'PhabricatorCalendarImportEngine' => 'Phobject',
+ 'PhabricatorCalendarImportICSFileTransaction' => 'PhabricatorCalendarImportTransactionType',
'PhabricatorCalendarImportListController' => 'PhabricatorCalendarController',
+ 'PhabricatorCalendarImportNameTransaction' => 'PhabricatorCalendarImportTransactionType',
'PhabricatorCalendarImportPHIDType' => 'PhabricatorPHIDType',
'PhabricatorCalendarImportQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorCalendarImportSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorCalendarImportTransaction' => 'PhabricatorModularTransaction',
'PhabricatorCalendarImportTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+ 'PhabricatorCalendarImportTransactionType' => 'PhabricatorModularTransactionType',
+ 'PhabricatorCalendarImportViewController' => 'PhabricatorCalendarController',
'PhabricatorCalendarRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'PhabricatorCalendarReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'PhabricatorCalendarSchemaSpec' => 'PhabricatorConfigSchemaSpec',
@@ -7448,6 +7469,7 @@
'PhabricatorFileDeleteController' => 'PhabricatorFileController',
'PhabricatorFileDropUploadController' => 'PhabricatorFileController',
'PhabricatorFileEditController' => 'PhabricatorFileController',
+ 'PhabricatorFileEditField' => 'PhabricatorEditField',
'PhabricatorFileEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorFileExternalRequest' => array(
'PhabricatorFileDAO',
diff --git a/src/aphront/httpparametertype/AphrontFileHTTPParameterType.php b/src/aphront/httpparametertype/AphrontFileHTTPParameterType.php
new file mode 100644
--- /dev/null
+++ b/src/aphront/httpparametertype/AphrontFileHTTPParameterType.php
@@ -0,0 +1,60 @@
+<?php
+
+final class AphrontFileHTTPParameterType
+ extends AphrontHTTPParameterType {
+
+ private function getFileKey($key) {
+ return $key.'_raw';
+ }
+
+ protected function getParameterExists(AphrontRequest $request, $key) {
+ $file_key = $this->getFileKey($key);
+ return $request->getExists($key) ||
+ $request->getFileExists($file_key);
+ }
+
+ protected function getParameterValue(AphrontRequest $request, $key) {
+ $value = $request->getStrList($key);
+ if ($value) {
+ return head($value);
+ }
+
+ // NOTE: At least for now, we'll attempt to read a direct upload if we
+ // miss on a PHID. Currently, PHUIFormFileControl does a client-side
+ // upload on workflow forms (which is good) but doesn't have a hook for
+ // non-workflow forms (which isn't as good). Giving it a hook is desirable,
+ // but complicated. Even if we do hook it, it may be reasonable to keep
+ // this code around as a fallback if the client-side JS goes awry.
+
+ $file_key = $this->getFileKey($key);
+ if (!$request->getFileExists($file_key)) {
+ return null;
+ }
+
+ $viewer = $this->getViewer();
+ $file = PhabricatorFile::newFromPHPUpload(
+ idx($_FILES, $file_key),
+ array(
+ 'authorPHID' => $viewer->getPHID(),
+ 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
+ ));
+ return $file->getPHID();
+ }
+
+ protected function getParameterTypeName() {
+ return 'file';
+ }
+
+ protected function getParameterFormatDescriptions() {
+ return array(
+ pht('A file PHID.'),
+ );
+ }
+
+ protected function getParameterExamples() {
+ return array(
+ 'v=PHID-FILE-wxyz',
+ );
+ }
+
+}
diff --git a/src/applications/calendar/controller/PhabricatorCalendarImportEditController.php b/src/applications/calendar/controller/PhabricatorCalendarImportEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/controller/PhabricatorCalendarImportEditController.php
@@ -0,0 +1,92 @@
+<?php
+
+final class PhabricatorCalendarImportEditController
+ extends PhabricatorCalendarController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $engine = id(new PhabricatorCalendarImportEditEngine())
+ ->setController($this);
+
+ $id = $request->getURIData('id');
+ if (!$id) {
+ $list_uri = $this->getApplicationURI('import/');
+
+ $import_type = $request->getStr('importType');
+ $import_engines = PhabricatorCalendarImportEngine::getAllImportEngines();
+ if (empty($import_engines[$import_type])) {
+ return $this->buildEngineTypeResponse($list_uri);
+ }
+
+ $import_engine = $import_engines[$import_type];
+
+ $engine
+ ->addContextParameter('importType', $import_type)
+ ->setImportEngine($import_engine);
+ }
+
+ return $engine->buildResponse();
+ }
+
+ private function buildEngineTypeResponse($cancel_uri) {
+ $import_engines = PhabricatorCalendarImportEngine::getAllImportEngines();
+
+ $request = $this->getRequest();
+ $viewer = $this->getViewer();
+
+ $e_import = null;
+ $errors = array();
+ if ($request->isFormPost()) {
+ $e_import = pht('Required');
+ $errors[] = pht(
+ 'To import events, you must select a source to import from.');
+ }
+
+ $type_control = id(new AphrontFormRadioButtonControl())
+ ->setLabel(pht('Import Type'))
+ ->setName('importType')
+ ->setError($e_import);
+
+ foreach ($import_engines as $import_engine) {
+ $type_control->addButton(
+ $import_engine->getImportEngineType(),
+ $import_engine->getImportEngineName(),
+ $import_engine->getImportEngineHint());
+ }
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb(pht('New Import'));
+ $crumbs->setBorder(true);
+
+ $title = pht('Choose Import Type');
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('New Import'))
+ ->setHeaderIcon('fa-upload');
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->appendChild($type_control)
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue(pht('Continue'))
+ ->addCancelButton($cancel_uri));
+
+ $box = id(new PHUIObjectBoxView())
+ ->setFormErrors($errors)
+ ->setHeaderText(pht('Import'))
+ ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
+ ->setForm($form);
+
+ $view = id(new PHUITwoColumnView())
+ ->setHeader($header)
+ ->setFooter(
+ array(
+ $box,
+ ));
+
+ return $this->newPage()
+ ->setTitle($title)
+ ->setCrumbs($crumbs)
+ ->appendChild($view);
+ }
+
+}
diff --git a/src/applications/calendar/controller/PhabricatorCalendarImportListController.php b/src/applications/calendar/controller/PhabricatorCalendarImportListController.php
--- a/src/applications/calendar/controller/PhabricatorCalendarImportListController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarImportListController.php
@@ -9,4 +9,17 @@
->buildResponse();
}
+ protected function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ $crumbs->addAction(
+ id(new PHUIListItemView())
+ ->setName(pht('Import Events'))
+ ->setHref($this->getApplicationURI('import/edit/'))
+ ->setIcon('fa-upload'));
+
+ return $crumbs;
+ }
+
+
}
diff --git a/src/applications/calendar/controller/PhabricatorCalendarImportViewController.php b/src/applications/calendar/controller/PhabricatorCalendarImportViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/controller/PhabricatorCalendarImportViewController.php
@@ -0,0 +1,130 @@
+<?php
+
+final class PhabricatorCalendarImportViewController
+ extends PhabricatorCalendarController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+
+ $import = id(new PhabricatorCalendarImportQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($request->getURIData('id')))
+ ->executeOne();
+ if (!$import) {
+ return new Aphront404Response();
+ }
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb(
+ pht('Imports'),
+ '/calendar/import/');
+ $crumbs->addTextCrumb(pht('Import %d', $import->getID()));
+ $crumbs->setBorder(true);
+
+ $timeline = $this->buildTransactionTimeline(
+ $import,
+ new PhabricatorCalendarImportTransactionQuery());
+ $timeline->setShouldTerminate(true);
+
+ $header = $this->buildHeaderView($import);
+ $curtain = $this->buildCurtain($import);
+ $details = $this->buildPropertySection($import);
+
+ $view = id(new PHUITwoColumnView())
+ ->setHeader($header)
+ ->setMainColumn(
+ array(
+ $timeline,
+ ))
+ ->setCurtain($curtain)
+ ->addPropertySection(pht('Details'), $details);
+
+ $page_title = pht(
+ 'Import %d %s',
+ $import->getID(),
+ $import->getDisplayName());
+
+ return $this->newPage()
+ ->setTitle($page_title)
+ ->setCrumbs($crumbs)
+ ->setPageObjectPHIDs(array($import->getPHID()))
+ ->appendChild($view);
+ }
+
+ private function buildHeaderView(
+ PhabricatorCalendarImport $import) {
+ $viewer = $this->getViewer();
+ $id = $import->getID();
+
+ if ($import->getIsDisabled()) {
+ $icon = 'fa-ban';
+ $color = 'red';
+ $status = pht('Disabled');
+ } else {
+ $icon = 'fa-check';
+ $color = 'bluegrey';
+ $status = pht('Active');
+ }
+
+ $header = id(new PHUIHeaderView())
+ ->setViewer($viewer)
+ ->setHeader($import->getDisplayName())
+ ->setStatus($icon, $color, $status)
+ ->setPolicyObject($import);
+
+ return $header;
+ }
+
+ private function buildCurtain(PhabricatorCalendarImport $import) {
+ $viewer = $this->getViewer();
+ $id = $import->getID();
+
+ $curtain = $this->newCurtainView($import);
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $import,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $edit_uri = "import/edit/{$id}/";
+ $edit_uri = $this->getApplicationURI($edit_uri);
+
+ $curtain->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Edit Import'))
+ ->setIcon('fa-pencil')
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
+ ->setHref($edit_uri));
+
+ $disable_uri = "import/disable/{$id}/";
+ $disable_uri = $this->getApplicationURI($disable_uri);
+ if ($import->getIsDisabled()) {
+ $disable_name = pht('Enable Import');
+ $disable_icon = 'fa-check';
+ } else {
+ $disable_name = pht('Disable Import');
+ $disable_icon = 'fa-ban';
+ }
+
+ $curtain->addAction(
+ id(new PhabricatorActionView())
+ ->setName($disable_name)
+ ->setIcon($disable_icon)
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(true)
+ ->setHref($disable_uri));
+
+ return $curtain;
+ }
+
+ private function buildPropertySection(
+ PhabricatorCalendarImport $import) {
+ $viewer = $this->getViewer();
+
+ $properties = id(new PHUIPropertyListView())
+ ->setViewer($viewer);
+
+ return $properties;
+ }
+}
diff --git a/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php
@@ -0,0 +1,115 @@
+<?php
+
+final class PhabricatorCalendarImportEditEngine
+ extends PhabricatorEditEngine {
+
+ const ENGINECONST = 'calendar.import';
+
+ private $importEngine;
+
+ public function setImportEngine(PhabricatorCalendarImportEngine $engine) {
+ $this->importEngine = $engine;
+ return $this;
+ }
+
+ public function getImportEngine() {
+ return $this->importEngine;
+ }
+
+ public function getEngineName() {
+ return pht('Calendar Imports');
+ }
+
+ public function isEngineConfigurable() {
+ return false;
+ }
+
+ public function getSummaryHeader() {
+ return pht('Configure Calendar Import Forms');
+ }
+
+ public function getSummaryText() {
+ return pht('Configure how users create and edit imports.');
+ }
+
+ public function getEngineApplicationClass() {
+ return 'PhabricatorCalendarApplication';
+ }
+
+ protected function newEditableObject() {
+ $viewer = $this->getViewer();
+ $engine = $this->getImportEngine();
+
+ return PhabricatorCalendarImport::initializeNewCalendarImport(
+ $viewer,
+ $engine);
+ }
+
+ protected function newObjectQuery() {
+ return new PhabricatorCalendarImportQuery();
+ }
+
+ protected function getObjectCreateTitleText($object) {
+ return pht('Create New Import');
+ }
+
+ protected function getObjectEditTitleText($object) {
+ return pht('Edit Import: %s', $object->getDisplayName());
+ }
+
+ protected function getObjectEditShortText($object) {
+ return pht('Import %d', $object->getID());
+ }
+
+ protected function getObjectCreateShortText() {
+ return pht('Create Import');
+ }
+
+ protected function getObjectName() {
+ return pht('Import');
+ }
+
+ protected function getObjectViewURI($object) {
+ return $object->getURI();
+ }
+
+ protected function getEditorURI() {
+ return $this->getApplication()->getApplicationURI('import/edit/');
+ }
+
+ protected function buildCustomEditFields($object) {
+ $viewer = $this->getViewer();
+
+ $fields = array(
+ id(new PhabricatorTextEditField())
+ ->setKey('name')
+ ->setLabel(pht('Name'))
+ ->setDescription(pht('Name of the import.'))
+ ->setTransactionType(
+ PhabricatorCalendarImportNameTransaction::TRANSACTIONTYPE)
+ ->setConduitDescription(pht('Rename the import.'))
+ ->setConduitTypeDescription(pht('New import name.'))
+ ->setValue($object->getName()),
+ id(new PhabricatorBoolEditField())
+ ->setKey('disabled')
+ ->setOptions(pht('Active'), pht('Disabled'))
+ ->setLabel(pht('Disabled'))
+ ->setDescription(pht('Disable the import.'))
+ ->setTransactionType(
+ PhabricatorCalendarImportDisableTransaction::TRANSACTIONTYPE)
+ ->setIsConduitOnly(true)
+ ->setConduitDescription(pht('Disable or restore the import.'))
+ ->setConduitTypeDescription(pht('True to cancel the import.'))
+ ->setValue($object->getIsDisabled()),
+ );
+
+ $import_engine = $object->getEngine();
+ foreach ($import_engine->newEditEngineFields($this, $object) as $field) {
+ $fields[] = $field;
+ }
+
+ return $fields;
+ }
+
+
+}
diff --git a/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php b/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php
--- a/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php
+++ b/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php
@@ -15,4 +15,28 @@
return pht('%s created this import.', $author);
}
+ public function getTransactionTypes() {
+ $types = parent::getTransactionTypes();
+
+ $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
+ $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
+
+ return $types;
+ }
+
+ protected function applyFinalEffects(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+
+ if ($this->getIsNewObject()) {
+ $actor = $this->getActor();
+
+ $import_engine = $object->getEngine();
+ $import_engine->didCreateImport($actor, $object);
+ }
+
+ return $xactions;
+ }
+
+
}
diff --git a/src/applications/calendar/import/PhabricatorCalendarICSImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarICSImportEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/import/PhabricatorCalendarICSImportEngine.php
@@ -0,0 +1,74 @@
+<?php
+
+final class PhabricatorCalendarICSImportEngine
+ extends PhabricatorCalendarImportEngine {
+
+ const ENGINETYPE = 'ics';
+
+ public function getImportEngineName() {
+ return pht('Import .ics File');
+ }
+
+ public function getImportEngineHint() {
+ return pht('Import an event in ".ics" (iCalendar) format.');
+ }
+
+ 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();
+
+ $parser = id(new PhutilICSParser());
+
+ $document = $parser->parseICSData($data);
+
+ return $this->importEventDocument($viewer, $import, $document);
+ }
+
+
+
+}
diff --git a/src/applications/calendar/import/PhabricatorCalendarImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarImportEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/import/PhabricatorCalendarImportEngine.php
@@ -0,0 +1,68 @@
+<?php
+
+abstract class PhabricatorCalendarImportEngine
+ extends Phobject {
+
+ final public function getImportEngineType() {
+ return $this->getPhobjectClassConstant('ENGINETYPE', 64);
+ }
+
+
+ abstract public function getImportEngineName();
+ abstract public function getImportEngineHint();
+
+ abstract public function newEditEngineFields(
+ PhabricatorEditEngine $engine,
+ PhabricatorCalendarImport $import);
+
+ abstract public function getDisplayName(PhabricatorCalendarImport $import);
+
+ abstract public function didCreateImport(
+ PhabricatorUser $viewer,
+ PhabricatorCalendarImport $import);
+
+ final public static function getAllImportEngines() {
+ return id(new PhutilClassMapQuery())
+ ->setAncestorClass(__CLASS__)
+ ->setUniqueMethod('getImportEngineType')
+ ->setSortMethod('getImportEngineName')
+ ->execute();
+ }
+
+ final protected function importEventDocument(
+ PhabricatorUser $viewer,
+ PhabricatorCalendarImport $import,
+ PhutilCalendarRootNode $root) {
+
+ $event_type = PhutilCalendarEventNode::NODETYPE;
+
+ $events = array();
+ foreach ($root->getChildren() as $document) {
+ foreach ($document->getChildren() as $node) {
+ if ($node->getNodeType() != $event_type) {
+ // TODO: Warn that we ignored this.
+ continue;
+ }
+
+ $event = PhabricatorCalendarEvent::newFromDocumentNode($viewer, $node);
+
+ $event
+ ->setImportAuthorPHID($viewer->getPHID())
+ ->setImportSourcePHID($import->getPHID())
+ ->attachImportSource($import);
+
+ $events[] = $event;
+ }
+ }
+
+ // TODO: Use transactions.
+ // TODO: Update existing events instead of fataling.
+ foreach ($events as $event) {
+ $event->save();
+ }
+
+ }
+
+
+
+}
diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php
--- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php
+++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php
@@ -468,9 +468,9 @@
->setViewer($viewer)
->withPHIDs($import_phids)
->execute();
- $sources = mpull($sources, null, 'getPHID');
+ $imports = mpull($imports, null, 'getPHID');
} else {
- $sources = array();
+ $imports = array();
}
foreach ($events as $key => $event) {
diff --git a/src/applications/calendar/query/PhabricatorCalendarImportQuery.php b/src/applications/calendar/query/PhabricatorCalendarImportQuery.php
--- a/src/applications/calendar/query/PhabricatorCalendarImportQuery.php
+++ b/src/applications/calendar/query/PhabricatorCalendarImportQuery.php
@@ -70,6 +70,24 @@
return $where;
}
+ protected function willFilterPage(array $page) {
+ $engines = PhabricatorCalendarImportEngine::getAllImportEngines();
+ foreach ($page as $key => $import) {
+ $engine_type = $import->getEngineType();
+ $engine = idx($engines, $engine_type);
+
+ if (!$engine) {
+ unset($page[$key]);
+ $this->didRejectResult($import);
+ continue;
+ }
+
+ $import->attachEngine(clone $engine);
+ }
+
+ return $page;
+ }
+
protected function getPrimaryTableAlias() {
return 'import';
}
diff --git a/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php
--- a/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php
+++ b/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php
@@ -62,7 +62,7 @@
$item = id(new PHUIObjectItemView())
->setViewer($viewer)
->setObjectName(pht('Import %d', $import->getID()))
- ->setHeader($import->getName())
+ ->setHeader($import->getDisplayName())
->setHref($import->getURI());
if ($import->getIsDisabled()) {
diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
--- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php
+++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
@@ -81,6 +81,7 @@
$datetime_end = $datetime_start->newRelativeDateTime('PT1H');
return id(new PhabricatorCalendarEvent())
+ ->setDescription('')
->setHostPHID($actor->getPHID())
->setIsCancelled(0)
->setIsAllDay(0)
@@ -101,6 +102,66 @@
->applyViewerTimezone($actor);
}
+ public static function newFromDocumentNode(
+ PhabricatorUser $actor,
+ PhutilCalendarEventNode $node) {
+ $timezone = $actor->getTimezoneIdentifier();
+
+ $uid = $node->getUID();
+
+ $name = $node->getName();
+ if (!strlen($name)) {
+ if (strlen($uid)) {
+ $name = pht('Unnamed Event "%s"', $node->getUID());
+ } else {
+ $name = pht('Unnamed Imported Event');
+ }
+ }
+
+ $description = $node->getDescription();
+
+ $instance_iso = $node->getRecurrenceID();
+ if (strlen($instance_iso)) {
+ $instance_datetime = PhutilCalendarAbsoluteDateTime::newFromISO8601(
+ $instance_iso);
+ $instance_epoch = $instance_datetime->getEpoch();
+ } else {
+ $instance_epoch = null;
+ }
+ $full_uid = $uid.'/'.$instance_epoch;
+
+ $start_datetime = $node->getStartDateTime()
+ ->setViewerTimezone($timezone);
+ $end_datetime = $node->getEndDateTime()
+ ->setViewerTimezone($timezone);
+
+ $rrule = $node->getRecurrenceRule();
+
+ $event = self::initializeNewCalendarEvent($actor)
+ ->setName($name)
+ ->setStartDateTime($start_datetime)
+ ->setEndDateTime($end_datetime)
+ ->setImportUID($full_uid)
+ ->setUTCInstanceEpoch($instance_epoch);
+
+ if (strlen($description)) {
+ $event->setDescription($description);
+ }
+
+ if ($rrule) {
+ $event->setRecurrenceRule($rrule);
+ $event->setIsRecurring(1);
+
+ $until_datetime = $rrule->getUntil()
+ ->setViewerTimezone($timezone);
+ if ($until_datetime) {
+ $event->setUntilDateTime($until_datetime);
+ }
+ }
+
+ return $event;
+ }
+
private function newChild(
PhabricatorUser $actor,
$sequence,
@@ -980,7 +1041,7 @@
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->getImportSource()) {
- return PhabricatorPolicy::POLICY_NOONE;
+ return PhabricatorPolicies::POLICY_NOONE;
} else {
return $this->getEditPolicy();
}
diff --git a/src/applications/calendar/storage/PhabricatorCalendarImport.php b/src/applications/calendar/storage/PhabricatorCalendarImport.php
--- a/src/applications/calendar/storage/PhabricatorCalendarImport.php
+++ b/src/applications/calendar/storage/PhabricatorCalendarImport.php
@@ -17,12 +17,16 @@
private $engine = self::ATTACHABLE;
- public static function initializeNewCalendarImport(PhabricatorUser $actor) {
+ public static function initializeNewCalendarImport(
+ PhabricatorUser $actor,
+ PhabricatorCalendarImportEngine $engine) {
return id(new self())
->setAuthorPHID($actor->getPHID())
->setViewPolicy($actor->getPHID())
->setEditPolicy($actor->getPHID())
- ->setIsDisabled(0);
+ ->setIsDisabled(0)
+ ->setEngineType($engine->getImportEngineType())
+ ->attachEngine($engine);
}
protected function getConfiguration() {
@@ -53,6 +57,33 @@
return "/calendar/import/{$id}/";
}
+ public function attachEngine(PhabricatorCalendarImportEngine $engine) {
+ $this->engine = $engine;
+ return $this;
+ }
+
+ public function getEngine() {
+ return $this->assertAttached($this->engine);
+ }
+
+ public function getParameter($key, $default = null) {
+ return idx($this->parameters, $key, $default);
+ }
+
+ public function setParameter($key, $value) {
+ $this->parameters[$key] = $value;
+ return $this;
+ }
+
+ public function getDisplayName() {
+ $name = $this->getName();
+ if (strlen($name)) {
+ return $name;
+ }
+
+ return $this->getEngine()->getDisplayName($this);
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
diff --git a/src/applications/calendar/xaction/PhabricatorCalendarImportDisableTransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarImportDisableTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/xaction/PhabricatorCalendarImportDisableTransaction.php
@@ -0,0 +1,28 @@
+<?php
+
+final class PhabricatorCalendarImportDisableTransaction
+ extends PhabricatorCalendarImportTransactionType {
+
+ const TRANSACTIONTYPE = 'calendar.import.disable';
+
+ public function generateOldValue($object) {
+ return (int)$object->getIsDisabled();
+ }
+
+ public function applyInternalEffects($object, $value) {
+ $object->setIsDisabled((int)$value);
+ }
+
+ public function getTitle() {
+ if ($this->getNewValue()) {
+ return pht(
+ '%s disabled this import.',
+ $this->renderAuthor());
+ } else {
+ return pht(
+ '%s enabled this import.',
+ $this->renderAuthor());
+ }
+ }
+
+}
diff --git a/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php
@@ -0,0 +1,80 @@
+<?php
+
+final class PhabricatorCalendarImportICSFileTransaction
+ extends PhabricatorCalendarImportTransactionType {
+
+ const TRANSACTIONTYPE = 'calendar.import.ics.file';
+ const PARAMKEY_FILE = 'ics.filePHID';
+ const PARAMKEY_NAME = 'ics.fileName';
+
+ public function generateOldValue($object) {
+ return $object->getParameter(self::PARAMKEY_FILE);
+ }
+
+ public function applyInternalEffects($object, $value) {
+ $object->setParameter(self::PARAMKEY_FILE, $value);
+
+ $viewer = $this->getActor();
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($value))
+ ->executeOne();
+ if ($file) {
+ $object->setParameter(self::PARAMKEY_NAME, $file->getName());
+ }
+ }
+
+ public function getTitle() {
+ return pht(
+ '%s imported an ICS file.',
+ $this->renderAuthor());
+ }
+
+ public function validateTransactions($object, array $xactions) {
+ $viewer = $this->getActor();
+ $errors = array();
+
+ $ics_type = PhabricatorCalendarICSImportEngine::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 file to an import type other than '.
+ 'an ICS import (type is "%s").',
+ $import_type));
+
+ return $errors;
+ }
+
+ $new_value = $object->getParameter(self::PARAMKEY_FILE);
+ foreach ($xactions as $xaction) {
+ $new_value = $xaction->getNewValue();
+ if (!strlen($new_value)) {
+ continue;
+ }
+
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($new_value))
+ ->executeOne();
+ if (!$file) {
+ $errors[] = $this->newInvalidError(
+ pht(
+ 'File PHID "%s" is not valid or not visible.',
+ $new_value),
+ $xaction);
+ }
+ }
+
+ if (!$new_value) {
+ $errors[] = $this->newRequiredError(
+ pht('You must select an ".ics" file to import.'));
+ }
+
+ return $errors;
+ }
+}
diff --git a/src/applications/calendar/xaction/PhabricatorCalendarImportNameTransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarImportNameTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/xaction/PhabricatorCalendarImportNameTransaction.php
@@ -0,0 +1,39 @@
+<?php
+
+final class PhabricatorCalendarImportNameTransaction
+ extends PhabricatorCalendarImportTransactionType {
+
+ const TRANSACTIONTYPE = 'calendar.import.name';
+
+ public function generateOldValue($object) {
+ return $object->getName();
+ }
+
+ public function applyInternalEffects($object, $value) {
+ $object->setName($value);
+ }
+
+ public function getTitle() {
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ if (!strlen($old)) {
+ return pht(
+ '%s named this import %s.',
+ $this->renderAuthor(),
+ $this->renderNewValue());
+ } else if (!strlen($new)) {
+ return pht(
+ '%s removed the name of this import (was: %s).',
+ $this->renderAuthor(),
+ $this->renderOldValue());
+ } else {
+ return pht(
+ '%s renamed this import from %s to %s.',
+ $this->renderAuthor(),
+ $this->renderOldValue(),
+ $this->renderNewValue());
+ }
+ }
+
+}
diff --git a/src/applications/calendar/xaction/PhabricatorCalendarImportTransactionType.php b/src/applications/calendar/xaction/PhabricatorCalendarImportTransactionType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/xaction/PhabricatorCalendarImportTransactionType.php
@@ -0,0 +1,4 @@
+<?php
+
+abstract class PhabricatorCalendarImportTransactionType
+ extends PhabricatorModularTransactionType {}
diff --git a/src/applications/transactions/editfield/PhabricatorFileEditField.php b/src/applications/transactions/editfield/PhabricatorFileEditField.php
new file mode 100644
--- /dev/null
+++ b/src/applications/transactions/editfield/PhabricatorFileEditField.php
@@ -0,0 +1,23 @@
+<?php
+
+final class PhabricatorFileEditField
+ extends PhabricatorEditField {
+
+ protected function newControl() {
+ return new PHUIFormFileControl();
+ }
+
+ protected function newHTTPParameterType() {
+ return new AphrontFileHTTPParameterType();
+ }
+
+ protected function newConduitParameterType() {
+ return new ConduitPHIDParameterType();
+ }
+
+ public function appendToForm(AphrontFormView $form) {
+ $form->setEncType('multipart/form-data');
+ return parent::appendToForm($form);
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Oct 15 2024, 5:35 AM (5 w, 1 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6711550
Default Alt Text
D16699.id40199.diff (37 KB)
Attached To
Mode
D16699: Make Calendar ICS imports sort of work in a crude, approximate way
Attached
Detach File
Event Timeline
Log In to Comment