Skip to content

Commit 86a00ee

Browse files
author
epriestley
committed
Make Calendar ICS imports sort of work in a crude, approximate way
Summary: Ref T10747. This barely works, but can technically import some event data. Test Plan: Used import flow to import a ".ics" document. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10747 Differential Revision: https://secure.phabricator.com/D16699
1 parent 2ab07ed commit 86a00ee

19 files changed

+888
-6
lines changed

src/__phutil_library_map__.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140
'AphrontDialogView' => 'view/AphrontDialogView.php',
141141
'AphrontEpochHTTPParameterType' => 'aphront/httpparametertype/AphrontEpochHTTPParameterType.php',
142142
'AphrontException' => 'aphront/exception/AphrontException.php',
143+
'AphrontFileHTTPParameterType' => 'aphront/httpparametertype/AphrontFileHTTPParameterType.php',
143144
'AphrontFileResponse' => 'aphront/response/AphrontFileResponse.php',
144145
'AphrontFormCheckboxControl' => 'view/form/control/AphrontFormCheckboxControl.php',
145146
'AphrontFormControl' => 'view/form/control/AphrontFormControl.php',
@@ -2098,16 +2099,25 @@
20982099
'PhabricatorCalendarExportViewController' => 'applications/calendar/controller/PhabricatorCalendarExportViewController.php',
20992100
'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php',
21002101
'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php',
2102+
'PhabricatorCalendarICSImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSImportEngine.php',
21012103
'PhabricatorCalendarICSWriter' => 'applications/calendar/util/PhabricatorCalendarICSWriter.php',
21022104
'PhabricatorCalendarIconSet' => 'applications/calendar/icon/PhabricatorCalendarIconSet.php',
21032105
'PhabricatorCalendarImport' => 'applications/calendar/storage/PhabricatorCalendarImport.php',
2106+
'PhabricatorCalendarImportDisableTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportDisableTransaction.php',
2107+
'PhabricatorCalendarImportEditController' => 'applications/calendar/controller/PhabricatorCalendarImportEditController.php',
2108+
'PhabricatorCalendarImportEditEngine' => 'applications/calendar/editor/PhabricatorCalendarImportEditEngine.php',
21042109
'PhabricatorCalendarImportEditor' => 'applications/calendar/editor/PhabricatorCalendarImportEditor.php',
2110+
'PhabricatorCalendarImportEngine' => 'applications/calendar/import/PhabricatorCalendarImportEngine.php',
2111+
'PhabricatorCalendarImportICSFileTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php',
21052112
'PhabricatorCalendarImportListController' => 'applications/calendar/controller/PhabricatorCalendarImportListController.php',
2113+
'PhabricatorCalendarImportNameTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportNameTransaction.php',
21062114
'PhabricatorCalendarImportPHIDType' => 'applications/calendar/phid/PhabricatorCalendarImportPHIDType.php',
21072115
'PhabricatorCalendarImportQuery' => 'applications/calendar/query/PhabricatorCalendarImportQuery.php',
21082116
'PhabricatorCalendarImportSearchEngine' => 'applications/calendar/query/PhabricatorCalendarImportSearchEngine.php',
21092117
'PhabricatorCalendarImportTransaction' => 'applications/calendar/storage/PhabricatorCalendarImportTransaction.php',
21102118
'PhabricatorCalendarImportTransactionQuery' => 'applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php',
2119+
'PhabricatorCalendarImportTransactionType' => 'applications/calendar/xaction/PhabricatorCalendarImportTransactionType.php',
2120+
'PhabricatorCalendarImportViewController' => 'applications/calendar/controller/PhabricatorCalendarImportViewController.php',
21112121
'PhabricatorCalendarRemarkupRule' => 'applications/calendar/remarkup/PhabricatorCalendarRemarkupRule.php',
21122122
'PhabricatorCalendarReplyHandler' => 'applications/calendar/mail/PhabricatorCalendarReplyHandler.php',
21132123
'PhabricatorCalendarSchemaSpec' => 'applications/calendar/storage/PhabricatorCalendarSchemaSpec.php',
@@ -2582,6 +2592,7 @@
25822592
'PhabricatorFileDeleteController' => 'applications/files/controller/PhabricatorFileDeleteController.php',
25832593
'PhabricatorFileDropUploadController' => 'applications/files/controller/PhabricatorFileDropUploadController.php',
25842594
'PhabricatorFileEditController' => 'applications/files/controller/PhabricatorFileEditController.php',
2595+
'PhabricatorFileEditField' => 'applications/transactions/editfield/PhabricatorFileEditField.php',
25852596
'PhabricatorFileEditor' => 'applications/files/editor/PhabricatorFileEditor.php',
25862597
'PhabricatorFileExternalRequest' => 'applications/files/storage/PhabricatorFileExternalRequest.php',
25872598
'PhabricatorFileExternalRequestGarbageCollector' => 'applications/files/garbagecollector/PhabricatorFileExternalRequestGarbageCollector.php',
@@ -4638,6 +4649,7 @@
46384649
),
46394650
'AphrontEpochHTTPParameterType' => 'AphrontHTTPParameterType',
46404651
'AphrontException' => 'Exception',
4652+
'AphrontFileHTTPParameterType' => 'AphrontHTTPParameterType',
46414653
'AphrontFileResponse' => 'AphrontResponse',
46424654
'AphrontFormCheckboxControl' => 'AphrontFormControl',
46434655
'AphrontFormControl' => 'AphrontView',
@@ -6886,6 +6898,7 @@
68866898
'PhabricatorCalendarExportViewController' => 'PhabricatorCalendarController',
68876899
'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO',
68886900
'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase',
6901+
'PhabricatorCalendarICSImportEngine' => 'PhabricatorCalendarImportEngine',
68896902
'PhabricatorCalendarICSWriter' => 'Phobject',
68906903
'PhabricatorCalendarIconSet' => 'PhabricatorIconSet',
68916904
'PhabricatorCalendarImport' => array(
@@ -6894,13 +6907,21 @@
68946907
'PhabricatorApplicationTransactionInterface',
68956908
'PhabricatorDestructibleInterface',
68966909
),
6910+
'PhabricatorCalendarImportDisableTransaction' => 'PhabricatorCalendarImportTransactionType',
6911+
'PhabricatorCalendarImportEditController' => 'PhabricatorCalendarController',
6912+
'PhabricatorCalendarImportEditEngine' => 'PhabricatorEditEngine',
68976913
'PhabricatorCalendarImportEditor' => 'PhabricatorApplicationTransactionEditor',
6914+
'PhabricatorCalendarImportEngine' => 'Phobject',
6915+
'PhabricatorCalendarImportICSFileTransaction' => 'PhabricatorCalendarImportTransactionType',
68986916
'PhabricatorCalendarImportListController' => 'PhabricatorCalendarController',
6917+
'PhabricatorCalendarImportNameTransaction' => 'PhabricatorCalendarImportTransactionType',
68996918
'PhabricatorCalendarImportPHIDType' => 'PhabricatorPHIDType',
69006919
'PhabricatorCalendarImportQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
69016920
'PhabricatorCalendarImportSearchEngine' => 'PhabricatorApplicationSearchEngine',
69026921
'PhabricatorCalendarImportTransaction' => 'PhabricatorModularTransaction',
69036922
'PhabricatorCalendarImportTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
6923+
'PhabricatorCalendarImportTransactionType' => 'PhabricatorModularTransactionType',
6924+
'PhabricatorCalendarImportViewController' => 'PhabricatorCalendarController',
69046925
'PhabricatorCalendarRemarkupRule' => 'PhabricatorObjectRemarkupRule',
69056926
'PhabricatorCalendarReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
69066927
'PhabricatorCalendarSchemaSpec' => 'PhabricatorConfigSchemaSpec',
@@ -7448,6 +7469,7 @@
74487469
'PhabricatorFileDeleteController' => 'PhabricatorFileController',
74497470
'PhabricatorFileDropUploadController' => 'PhabricatorFileController',
74507471
'PhabricatorFileEditController' => 'PhabricatorFileController',
7472+
'PhabricatorFileEditField' => 'PhabricatorEditField',
74517473
'PhabricatorFileEditor' => 'PhabricatorApplicationTransactionEditor',
74527474
'PhabricatorFileExternalRequest' => array(
74537475
'PhabricatorFileDAO',
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
final class AphrontFileHTTPParameterType
4+
extends AphrontHTTPParameterType {
5+
6+
private function getFileKey($key) {
7+
return $key.'_raw';
8+
}
9+
10+
protected function getParameterExists(AphrontRequest $request, $key) {
11+
$file_key = $this->getFileKey($key);
12+
return $request->getExists($key) ||
13+
$request->getFileExists($file_key);
14+
}
15+
16+
protected function getParameterValue(AphrontRequest $request, $key) {
17+
$value = $request->getStrList($key);
18+
if ($value) {
19+
return head($value);
20+
}
21+
22+
// NOTE: At least for now, we'll attempt to read a direct upload if we
23+
// miss on a PHID. Currently, PHUIFormFileControl does a client-side
24+
// upload on workflow forms (which is good) but doesn't have a hook for
25+
// non-workflow forms (which isn't as good). Giving it a hook is desirable,
26+
// but complicated. Even if we do hook it, it may be reasonable to keep
27+
// this code around as a fallback if the client-side JS goes awry.
28+
29+
$file_key = $this->getFileKey($key);
30+
if (!$request->getFileExists($file_key)) {
31+
return null;
32+
}
33+
34+
$viewer = $this->getViewer();
35+
$file = PhabricatorFile::newFromPHPUpload(
36+
idx($_FILES, $file_key),
37+
array(
38+
'authorPHID' => $viewer->getPHID(),
39+
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
40+
));
41+
return $file->getPHID();
42+
}
43+
44+
protected function getParameterTypeName() {
45+
return 'file';
46+
}
47+
48+
protected function getParameterFormatDescriptions() {
49+
return array(
50+
pht('A file PHID.'),
51+
);
52+
}
53+
54+
protected function getParameterExamples() {
55+
return array(
56+
'v=PHID-FILE-wxyz',
57+
);
58+
}
59+
60+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
final class PhabricatorCalendarImportEditController
4+
extends PhabricatorCalendarController {
5+
6+
public function handleRequest(AphrontRequest $request) {
7+
$engine = id(new PhabricatorCalendarImportEditEngine())
8+
->setController($this);
9+
10+
$id = $request->getURIData('id');
11+
if (!$id) {
12+
$list_uri = $this->getApplicationURI('import/');
13+
14+
$import_type = $request->getStr('importType');
15+
$import_engines = PhabricatorCalendarImportEngine::getAllImportEngines();
16+
if (empty($import_engines[$import_type])) {
17+
return $this->buildEngineTypeResponse($list_uri);
18+
}
19+
20+
$import_engine = $import_engines[$import_type];
21+
22+
$engine
23+
->addContextParameter('importType', $import_type)
24+
->setImportEngine($import_engine);
25+
}
26+
27+
return $engine->buildResponse();
28+
}
29+
30+
private function buildEngineTypeResponse($cancel_uri) {
31+
$import_engines = PhabricatorCalendarImportEngine::getAllImportEngines();
32+
33+
$request = $this->getRequest();
34+
$viewer = $this->getViewer();
35+
36+
$e_import = null;
37+
$errors = array();
38+
if ($request->isFormPost()) {
39+
$e_import = pht('Required');
40+
$errors[] = pht(
41+
'To import events, you must select a source to import from.');
42+
}
43+
44+
$type_control = id(new AphrontFormRadioButtonControl())
45+
->setLabel(pht('Import Type'))
46+
->setName('importType')
47+
->setError($e_import);
48+
49+
foreach ($import_engines as $import_engine) {
50+
$type_control->addButton(
51+
$import_engine->getImportEngineType(),
52+
$import_engine->getImportEngineName(),
53+
$import_engine->getImportEngineHint());
54+
}
55+
56+
$crumbs = $this->buildApplicationCrumbs();
57+
$crumbs->addTextCrumb(pht('New Import'));
58+
$crumbs->setBorder(true);
59+
60+
$title = pht('Choose Import Type');
61+
$header = id(new PHUIHeaderView())
62+
->setHeader(pht('New Import'))
63+
->setHeaderIcon('fa-upload');
64+
65+
$form = id(new AphrontFormView())
66+
->setUser($viewer)
67+
->appendChild($type_control)
68+
->appendChild(
69+
id(new AphrontFormSubmitControl())
70+
->setValue(pht('Continue'))
71+
->addCancelButton($cancel_uri));
72+
73+
$box = id(new PHUIObjectBoxView())
74+
->setFormErrors($errors)
75+
->setHeaderText(pht('Import'))
76+
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
77+
->setForm($form);
78+
79+
$view = id(new PHUITwoColumnView())
80+
->setHeader($header)
81+
->setFooter(
82+
array(
83+
$box,
84+
));
85+
86+
return $this->newPage()
87+
->setTitle($title)
88+
->setCrumbs($crumbs)
89+
->appendChild($view);
90+
}
91+
92+
}

src/applications/calendar/controller/PhabricatorCalendarImportListController.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,17 @@ public function handleRequest(AphrontRequest $request) {
99
->buildResponse();
1010
}
1111

12+
protected function buildApplicationCrumbs() {
13+
$crumbs = parent::buildApplicationCrumbs();
14+
15+
$crumbs->addAction(
16+
id(new PHUIListItemView())
17+
->setName(pht('Import Events'))
18+
->setHref($this->getApplicationURI('import/edit/'))
19+
->setIcon('fa-upload'));
20+
21+
return $crumbs;
22+
}
23+
24+
1225
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<?php
2+
3+
final class PhabricatorCalendarImportViewController
4+
extends PhabricatorCalendarController {
5+
6+
public function handleRequest(AphrontRequest $request) {
7+
$viewer = $request->getViewer();
8+
9+
$import = id(new PhabricatorCalendarImportQuery())
10+
->setViewer($viewer)
11+
->withIDs(array($request->getURIData('id')))
12+
->executeOne();
13+
if (!$import) {
14+
return new Aphront404Response();
15+
}
16+
17+
$crumbs = $this->buildApplicationCrumbs();
18+
$crumbs->addTextCrumb(
19+
pht('Imports'),
20+
'/calendar/import/');
21+
$crumbs->addTextCrumb(pht('Import %d', $import->getID()));
22+
$crumbs->setBorder(true);
23+
24+
$timeline = $this->buildTransactionTimeline(
25+
$import,
26+
new PhabricatorCalendarImportTransactionQuery());
27+
$timeline->setShouldTerminate(true);
28+
29+
$header = $this->buildHeaderView($import);
30+
$curtain = $this->buildCurtain($import);
31+
$details = $this->buildPropertySection($import);
32+
33+
$view = id(new PHUITwoColumnView())
34+
->setHeader($header)
35+
->setMainColumn(
36+
array(
37+
$timeline,
38+
))
39+
->setCurtain($curtain)
40+
->addPropertySection(pht('Details'), $details);
41+
42+
$page_title = pht(
43+
'Import %d %s',
44+
$import->getID(),
45+
$import->getDisplayName());
46+
47+
return $this->newPage()
48+
->setTitle($page_title)
49+
->setCrumbs($crumbs)
50+
->setPageObjectPHIDs(array($import->getPHID()))
51+
->appendChild($view);
52+
}
53+
54+
private function buildHeaderView(
55+
PhabricatorCalendarImport $import) {
56+
$viewer = $this->getViewer();
57+
$id = $import->getID();
58+
59+
if ($import->getIsDisabled()) {
60+
$icon = 'fa-ban';
61+
$color = 'red';
62+
$status = pht('Disabled');
63+
} else {
64+
$icon = 'fa-check';
65+
$color = 'bluegrey';
66+
$status = pht('Active');
67+
}
68+
69+
$header = id(new PHUIHeaderView())
70+
->setViewer($viewer)
71+
->setHeader($import->getDisplayName())
72+
->setStatus($icon, $color, $status)
73+
->setPolicyObject($import);
74+
75+
return $header;
76+
}
77+
78+
private function buildCurtain(PhabricatorCalendarImport $import) {
79+
$viewer = $this->getViewer();
80+
$id = $import->getID();
81+
82+
$curtain = $this->newCurtainView($import);
83+
84+
$can_edit = PhabricatorPolicyFilter::hasCapability(
85+
$viewer,
86+
$import,
87+
PhabricatorPolicyCapability::CAN_EDIT);
88+
89+
$edit_uri = "import/edit/{$id}/";
90+
$edit_uri = $this->getApplicationURI($edit_uri);
91+
92+
$curtain->addAction(
93+
id(new PhabricatorActionView())
94+
->setName(pht('Edit Import'))
95+
->setIcon('fa-pencil')
96+
->setDisabled(!$can_edit)
97+
->setWorkflow(!$can_edit)
98+
->setHref($edit_uri));
99+
100+
$disable_uri = "import/disable/{$id}/";
101+
$disable_uri = $this->getApplicationURI($disable_uri);
102+
if ($import->getIsDisabled()) {
103+
$disable_name = pht('Enable Import');
104+
$disable_icon = 'fa-check';
105+
} else {
106+
$disable_name = pht('Disable Import');
107+
$disable_icon = 'fa-ban';
108+
}
109+
110+
$curtain->addAction(
111+
id(new PhabricatorActionView())
112+
->setName($disable_name)
113+
->setIcon($disable_icon)
114+
->setDisabled(!$can_edit)
115+
->setWorkflow(true)
116+
->setHref($disable_uri));
117+
118+
return $curtain;
119+
}
120+
121+
private function buildPropertySection(
122+
PhabricatorCalendarImport $import) {
123+
$viewer = $this->getViewer();
124+
125+
$properties = id(new PHUIPropertyListView())
126+
->setViewer($viewer);
127+
128+
return $properties;
129+
}
130+
}

0 commit comments

Comments
 (0)