Page MenuHomePhabricator

D16699.id40199.diff
No OneTemporary

D16699.id40199.diff

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

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)

Event Timeline