@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorCalendarExportICSController
4 extends PhabricatorCalendarController {
5
6 public function shouldRequireLogin() {
7 // Export URIs are available if you know the secret key. We can't do any
8 // other kind of authentication because third-party applications like
9 // Google Calendar and Calendar.app need to be able to fetch these URIs.
10 return false;
11 }
12
13 public function handleRequest(AphrontRequest $request) {
14 $omnipotent = PhabricatorUser::getOmnipotentUser();
15
16 // NOTE: We're using the omnipotent viewer to fetch the export, but the
17 // URI must contain the secret key. Once we load the export we'll figure
18 // out who the effective viewer is.
19 $export = id(new PhabricatorCalendarExportQuery())
20 ->setViewer($omnipotent)
21 ->withSecretKeys(array($request->getURIData('secretKey')))
22 ->executeOne();
23 if (!$export) {
24 return new Aphront404Response();
25 }
26
27 if ($export->getIsDisabled()) {
28 return new Aphront404Response();
29 }
30
31 $author = id(new PhabricatorPeopleQuery())
32 ->setViewer($omnipotent)
33 ->withPHIDs(array($export->getAuthorPHID()))
34 ->needUserSettings(true)
35 ->executeOne();
36 if (!$author) {
37 return new Aphront404Response();
38 }
39
40 $mode = $export->getPolicyMode();
41 switch ($mode) {
42 case PhabricatorCalendarExport::MODE_PUBLIC:
43 $viewer = new PhabricatorUser();
44 break;
45 case PhabricatorCalendarExport::MODE_PRIVILEGED:
46 $viewer = $author;
47 break;
48 default:
49 throw new Exception(
50 pht(
51 'This export has an invalid mode ("%s").',
52 $mode));
53 }
54
55 $engine = id(new PhabricatorCalendarEventSearchEngine())
56 ->setViewer($viewer);
57
58 $query_key = $export->getQueryKey();
59 $saved = id(new PhabricatorSavedQueryQuery())
60 ->setViewer($omnipotent)
61 ->withEngineClassNames(array(get_class($engine)))
62 ->withQueryKeys(array($query_key))
63 ->executeOne();
64 if (!$saved) {
65 $saved = $engine->buildSavedQueryFromBuiltin($query_key);
66 }
67
68 if (!$saved) {
69 return new Aphront404Response();
70 }
71
72 $saved = clone $saved;
73
74 // Mark this as a query for export, so we get the correct ghost/recurring
75 // behaviors. We also want to load all matching events.
76 $saved->setParameter('export', true);
77 $saved->setParameter('limit', 0xFFFF);
78
79 // Remove any range constraints. We always export all matching events into
80 // ICS files.
81 $saved->setParameter('rangeStart', null);
82 $saved->setParameter('rangeEnd', null);
83 $saved->setParameter('upcoming', null);
84
85 // The "month" and "day" display modes imply time ranges.
86 $saved->setParameter('display', 'list');
87
88 $query = $engine->buildQueryFromSavedQuery($saved);
89
90 $events = $query
91 ->setViewer($viewer)
92 ->execute();
93
94 return $this->newICSResponse(
95 $viewer,
96 $export->getICSFilename(),
97 $events);
98 }
99
100}