@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
3/**
4 * @task action Handling Action Requests
5 */
6abstract class NuanceSourceDefinition extends Phobject {
7
8 private $viewer;
9 private $source;
10
11 public function setViewer(PhabricatorUser $viewer) {
12 $this->viewer = $viewer;
13 return $this;
14 }
15
16 public function getViewer() {
17 if (!$this->viewer) {
18 throw new PhutilInvalidStateException('setViewer');
19 }
20 return $this->viewer;
21 }
22
23 public function setSource(NuanceSource $source) {
24 $this->source = $source;
25 return $this;
26 }
27
28 public function getSource() {
29 if (!$this->source) {
30 throw new PhutilInvalidStateException('setSource');
31 }
32 return $this->source;
33 }
34
35 public function getSourceViewActions(AphrontRequest $request) {
36 return array();
37 }
38
39 public static function getAllDefinitions() {
40 return id(new PhutilClassMapQuery())
41 ->setAncestorClass(self::class)
42 ->setUniqueMethod('getSourceTypeConstant')
43 ->execute();
44 }
45
46 public function hasImportCursors() {
47 return false;
48 }
49
50 final public function getImportCursors() {
51 if (!$this->hasImportCursors()) {
52 throw new Exception(
53 pht('This source has no input cursors.'));
54 }
55
56 $viewer = PhabricatorUser::getOmnipotentUser();
57 $source = $this->getSource();
58 $cursors = $this->newImportCursors();
59
60 $data = id(new NuanceImportCursorDataQuery())
61 ->setViewer($viewer)
62 ->withSourcePHIDs(array($source->getPHID()))
63 ->execute();
64 $data = mpull($data, null, 'getCursorKey');
65
66 $map = array();
67 foreach ($cursors as $cursor) {
68 if (!($cursor instanceof NuanceImportCursor)) {
69 throw new Exception(
70 pht(
71 'Source "%s" (of class "%s") returned an invalid value from '.
72 'method "%s": all values must be objects of class "%s".',
73 $this->getName(),
74 get_class($this),
75 'newImportCursors()',
76 'NuanceImportCursor'));
77 }
78
79 $key = $cursor->getCursorKey();
80 if (!strlen($key)) {
81 throw new Exception(
82 pht(
83 'Source "%s" (of class "%s") returned an import cursor with '.
84 'a missing key from "%s". Each cursor must have a unique, '.
85 'nonempty key.',
86 $this->getName(),
87 get_class($this),
88 'newImportCursors()'));
89 }
90
91 $other = idx($map, $key);
92 if ($other) {
93 throw new Exception(
94 pht(
95 'Source "%s" (of class "%s") returned two cursors from method '.
96 '"%s" with the same key ("%s"). Each cursor must have a unique '.
97 'key.',
98 $this->getName(),
99 get_class($this),
100 'newImportCursors()',
101 $key));
102 }
103
104 $map[$key] = $cursor;
105
106 $cursor_data = idx($data, $key);
107 if (!$cursor_data) {
108 $cursor_data = $cursor->newEmptyCursorData($source);
109 }
110
111 $cursor
112 ->setViewer($viewer)
113 ->setSource($source)
114 ->setCursorData($cursor_data);
115 }
116
117 return $map;
118 }
119
120 protected function newImportCursors() {
121 throw new PhutilMethodNotImplementedException();
122 }
123
124 /**
125 * A human readable string like "Twitter" or "Phabricator Form".
126 */
127 abstract public function getName();
128
129
130 /**
131 * Human readable description of this source, a sentence or two long.
132 */
133 abstract public function getSourceDescription();
134
135 /**
136 * This should be a any VARCHAR(32).
137 *
138 * @{method:getAllDefinitions} will throw if you choose a string that
139 * collides with another @{class:NuanceSourceDefinition} class.
140 */
141 abstract public function getSourceTypeConstant();
142
143 public function renderView() {
144 return null;
145 }
146
147 public function renderListView() {
148 return null;
149 }
150
151 protected function newItemFromProperties(
152 $item_type,
153 $author_phid,
154 array $properties,
155 PhabricatorContentSource $content_source) {
156
157 // TODO: Should we have a tighter actor/viewer model? Requestors will
158 // often have no real user associated with them...
159 $actor = PhabricatorUser::getOmnipotentUser();
160 $source = $this->getSource();
161
162 $item = NuanceItem::initializeNewItem($item_type);
163
164 $xactions = array();
165
166 $xactions[] = id(new NuanceItemTransaction())
167 ->setTransactionType(NuanceItemSourceTransaction::TRANSACTIONTYPE)
168 ->setNewValue($source->getPHID());
169
170 // TODO: Eventually, apply real routing rules. For now, just put everything
171 // in the default queue for the source.
172 $xactions[] = id(new NuanceItemTransaction())
173 ->setTransactionType(NuanceItemQueueTransaction::TRANSACTIONTYPE)
174 ->setNewValue($source->getDefaultQueuePHID());
175
176 // TODO: Maybe this should all be modular transactions now?
177 foreach ($properties as $key => $property) {
178 $xactions[] = id(new NuanceItemTransaction())
179 ->setTransactionType(NuanceItemPropertyTransaction::TRANSACTIONTYPE)
180 ->setMetadataValue(NuanceItemTransaction::PROPERTY_KEY, $key)
181 ->setNewValue($property);
182 }
183
184 $editor = id(new NuanceItemEditor())
185 ->setActor($actor)
186 ->setActingAsPHID($author_phid)
187 ->setContentSource($content_source);
188
189 $editor->applyTransactions($item, $xactions);
190
191 return $item;
192 }
193
194 public function renderItemEditProperties(
195 PhabricatorUser $viewer,
196 NuanceItem $item,
197 PHUIPropertyListView $view) {
198 return;
199 }
200
201
202/* -( Handling Action Requests )------------------------------------------- */
203
204
205 public function handleActionRequest(AphrontRequest $request) {
206 return new Aphront404Response();
207 }
208
209 public function getActionURI($path = '') {
210 $source_id = $this->getSource()->getID();
211 return '/action/'.$source_id.'/'.ltrim($path, '/');
212 }
213
214}