Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/conduit/method/ConduitAPIMethod.php
12256 views
1
<?php
2
3
/**
4
* @task info Method Information
5
* @task status Method Status
6
* @task pager Paging Results
7
*/
8
abstract class ConduitAPIMethod
9
extends Phobject
10
implements PhabricatorPolicyInterface {
11
12
private $viewer;
13
14
const METHOD_STATUS_STABLE = 'stable';
15
const METHOD_STATUS_UNSTABLE = 'unstable';
16
const METHOD_STATUS_DEPRECATED = 'deprecated';
17
const METHOD_STATUS_FROZEN = 'frozen';
18
19
const SCOPE_NEVER = 'scope.never';
20
const SCOPE_ALWAYS = 'scope.always';
21
22
/**
23
* Get a short, human-readable text summary of the method.
24
*
25
* @return string Short summary of method.
26
* @task info
27
*/
28
public function getMethodSummary() {
29
return $this->getMethodDescription();
30
}
31
32
33
/**
34
* Get a detailed description of the method.
35
*
36
* This method should return remarkup.
37
*
38
* @return string Detailed description of the method.
39
* @task info
40
*/
41
abstract public function getMethodDescription();
42
43
final public function getDocumentationPages(PhabricatorUser $viewer) {
44
$pages = $this->newDocumentationPages($viewer);
45
return $pages;
46
}
47
48
protected function newDocumentationPages(PhabricatorUser $viewer) {
49
return array();
50
}
51
52
final protected function newDocumentationPage(PhabricatorUser $viewer) {
53
return id(new ConduitAPIDocumentationPage())
54
->setIconIcon('fa-chevron-right');
55
}
56
57
final protected function newDocumentationBoxPage(
58
PhabricatorUser $viewer,
59
$title,
60
$content) {
61
62
$box_view = id(new PHUIObjectBoxView())
63
->setHeaderText($title)
64
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
65
->setTable($content);
66
67
return $this->newDocumentationPage($viewer)
68
->setName($title)
69
->setContent($box_view);
70
}
71
72
abstract protected function defineParamTypes();
73
abstract protected function defineReturnType();
74
75
protected function defineErrorTypes() {
76
return array();
77
}
78
79
abstract protected function execute(ConduitAPIRequest $request);
80
81
public function isInternalAPI() {
82
return false;
83
}
84
85
public function getParamTypes() {
86
$types = $this->defineParamTypes();
87
88
$query = $this->newQueryObject();
89
if ($query) {
90
$types['order'] = 'optional order';
91
$types += $this->getPagerParamTypes();
92
}
93
94
return $types;
95
}
96
97
public function getReturnType() {
98
return $this->defineReturnType();
99
}
100
101
public function getErrorTypes() {
102
return $this->defineErrorTypes();
103
}
104
105
/**
106
* This is mostly for compatibility with
107
* @{class:PhabricatorCursorPagedPolicyAwareQuery}.
108
*/
109
public function getID() {
110
return $this->getAPIMethodName();
111
}
112
113
/**
114
* Get the status for this method (e.g., stable, unstable or deprecated).
115
* Should return a METHOD_STATUS_* constant. By default, methods are
116
* "stable".
117
*
118
* @return const METHOD_STATUS_* constant.
119
* @task status
120
*/
121
public function getMethodStatus() {
122
return self::METHOD_STATUS_STABLE;
123
}
124
125
/**
126
* Optional description to supplement the method status. In particular, if
127
* a method is deprecated, you can return a string here describing the reason
128
* for deprecation and stable alternatives.
129
*
130
* @return string|null Description of the method status, if available.
131
* @task status
132
*/
133
public function getMethodStatusDescription() {
134
return null;
135
}
136
137
public function getErrorDescription($error_code) {
138
return idx($this->getErrorTypes(), $error_code, pht('Unknown Error'));
139
}
140
141
public function getRequiredScope() {
142
return self::SCOPE_NEVER;
143
}
144
145
public function executeMethod(ConduitAPIRequest $request) {
146
$this->setViewer($request->getUser());
147
148
$client = $this->newConduitCallProxyClient($request);
149
if ($client) {
150
// We're proxying, so just make an intracluster call.
151
return $client->callMethodSynchronous(
152
$this->getAPIMethodName(),
153
$request->getAllParameters());
154
}
155
156
return $this->execute($request);
157
}
158
159
protected function newConduitCallProxyClient(ConduitAPIRequest $request) {
160
return null;
161
}
162
163
abstract public function getAPIMethodName();
164
165
/**
166
* Return a key which sorts methods by application name, then method status,
167
* then method name.
168
*/
169
public function getSortOrder() {
170
$name = $this->getAPIMethodName();
171
172
$map = array(
173
self::METHOD_STATUS_STABLE => 0,
174
self::METHOD_STATUS_UNSTABLE => 1,
175
self::METHOD_STATUS_DEPRECATED => 2,
176
);
177
$ord = idx($map, $this->getMethodStatus(), 0);
178
179
list($head, $tail) = explode('.', $name, 2);
180
181
return "{$head}.{$ord}.{$tail}";
182
}
183
184
public static function getMethodStatusMap() {
185
$map = array(
186
self::METHOD_STATUS_STABLE => pht('Stable'),
187
self::METHOD_STATUS_UNSTABLE => pht('Unstable'),
188
self::METHOD_STATUS_DEPRECATED => pht('Deprecated'),
189
);
190
191
return $map;
192
}
193
194
public function getApplicationName() {
195
return head(explode('.', $this->getAPIMethodName(), 2));
196
}
197
198
public static function loadAllConduitMethods() {
199
return self::newClassMapQuery()->execute();
200
}
201
202
private static function newClassMapQuery() {
203
return id(new PhutilClassMapQuery())
204
->setAncestorClass(__CLASS__)
205
->setUniqueMethod('getAPIMethodName');
206
}
207
208
public static function getConduitMethod($method_name) {
209
return id(new PhabricatorCachedClassMapQuery())
210
->setClassMapQuery(self::newClassMapQuery())
211
->setMapKeyMethod('getAPIMethodName')
212
->loadClass($method_name);
213
}
214
215
public function shouldRequireAuthentication() {
216
return true;
217
}
218
219
public function shouldAllowPublic() {
220
return false;
221
}
222
223
public function shouldAllowUnguardedWrites() {
224
return false;
225
}
226
227
228
/**
229
* Optionally, return a @{class:PhabricatorApplication} which this call is
230
* part of. The call will be disabled when the application is uninstalled.
231
*
232
* @return PhabricatorApplication|null Related application.
233
*/
234
public function getApplication() {
235
return null;
236
}
237
238
protected function formatStringConstants($constants) {
239
foreach ($constants as $key => $value) {
240
$constants[$key] = '"'.$value.'"';
241
}
242
$constants = implode(', ', $constants);
243
return 'string-constant<'.$constants.'>';
244
}
245
246
public static function getParameterMetadataKey($key) {
247
if (strncmp($key, 'api.', 4) === 0) {
248
// All keys passed beginning with "api." are always metadata keys.
249
return substr($key, 4);
250
} else {
251
switch ($key) {
252
// These are real keys which always belong to request metadata.
253
case 'access_token':
254
case 'scope':
255
case 'output':
256
257
// This is not a real metadata key; it is included here only to
258
// prevent Conduit methods from defining it.
259
case '__conduit__':
260
261
// This is prevented globally as a blanket defense against OAuth
262
// redirection attacks. It is included here to stop Conduit methods
263
// from defining it.
264
case 'code':
265
266
// This is not a real metadata key, but the presence of this
267
// parameter triggers an alternate request decoding pathway.
268
case 'params':
269
return $key;
270
}
271
}
272
273
return null;
274
}
275
276
final public function setViewer(PhabricatorUser $viewer) {
277
$this->viewer = $viewer;
278
return $this;
279
}
280
281
final public function getViewer() {
282
return $this->viewer;
283
}
284
285
/* -( Paging Results )----------------------------------------------------- */
286
287
288
/**
289
* @task pager
290
*/
291
protected function getPagerParamTypes() {
292
return array(
293
'before' => 'optional string',
294
'after' => 'optional string',
295
'limit' => 'optional int (default = 100)',
296
);
297
}
298
299
300
/**
301
* @task pager
302
*/
303
protected function newPager(ConduitAPIRequest $request) {
304
$limit = $request->getValue('limit', 100);
305
$limit = min(1000, $limit);
306
$limit = max(1, $limit);
307
308
$pager = id(new AphrontCursorPagerView())
309
->setPageSize($limit);
310
311
$before_id = $request->getValue('before');
312
if ($before_id !== null) {
313
$pager->setBeforeID($before_id);
314
}
315
316
$after_id = $request->getValue('after');
317
if ($after_id !== null) {
318
$pager->setAfterID($after_id);
319
}
320
321
return $pager;
322
}
323
324
325
/**
326
* @task pager
327
*/
328
protected function addPagerResults(
329
array $results,
330
AphrontCursorPagerView $pager) {
331
332
$results['cursor'] = array(
333
'limit' => $pager->getPageSize(),
334
'after' => $pager->getNextPageID(),
335
'before' => $pager->getPrevPageID(),
336
);
337
338
return $results;
339
}
340
341
342
/* -( Implementing Query Methods )----------------------------------------- */
343
344
345
public function newQueryObject() {
346
return null;
347
}
348
349
350
protected function newQueryForRequest(ConduitAPIRequest $request) {
351
$query = $this->newQueryObject();
352
353
if (!$query) {
354
throw new Exception(
355
pht(
356
'You can not call newQueryFromRequest() in this method ("%s") '.
357
'because it does not implement newQueryObject().',
358
get_class($this)));
359
}
360
361
if (!($query instanceof PhabricatorCursorPagedPolicyAwareQuery)) {
362
throw new Exception(
363
pht(
364
'Call to method newQueryObject() did not return an object of class '.
365
'"%s".',
366
'PhabricatorCursorPagedPolicyAwareQuery'));
367
}
368
369
$query->setViewer($request->getUser());
370
371
$order = $request->getValue('order');
372
if ($order !== null) {
373
if (is_scalar($order)) {
374
$query->setOrder($order);
375
} else {
376
$query->setOrderVector($order);
377
}
378
}
379
380
return $query;
381
}
382
383
384
/* -( PhabricatorPolicyInterface )----------------------------------------- */
385
386
387
public function getPHID() {
388
return null;
389
}
390
391
public function getCapabilities() {
392
return array(
393
PhabricatorPolicyCapability::CAN_VIEW,
394
);
395
}
396
397
public function getPolicy($capability) {
398
// Application methods get application visibility; other methods get open
399
// visibility.
400
401
$application = $this->getApplication();
402
if ($application) {
403
return $application->getPolicy($capability);
404
}
405
406
return PhabricatorPolicies::getMostOpenPolicy();
407
}
408
409
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
410
if (!$this->shouldRequireAuthentication()) {
411
// Make unauthenticated methods universally visible.
412
return true;
413
}
414
415
return false;
416
}
417
418
protected function hasApplicationCapability(
419
$capability,
420
PhabricatorUser $viewer) {
421
422
$application = $this->getApplication();
423
424
if (!$application) {
425
return false;
426
}
427
428
return PhabricatorPolicyFilter::hasCapability(
429
$viewer,
430
$application,
431
$capability);
432
}
433
434
protected function requireApplicationCapability(
435
$capability,
436
PhabricatorUser $viewer) {
437
438
$application = $this->getApplication();
439
if (!$application) {
440
return;
441
}
442
443
PhabricatorPolicyFilter::requireCapability(
444
$viewer,
445
$this->getApplication(),
446
$capability);
447
}
448
449
final protected function newRemarkupDocumentationView($remarkup) {
450
$viewer = $this->getViewer();
451
452
$view = new PHUIRemarkupView($viewer, $remarkup);
453
454
$view->setRemarkupOptions(
455
array(
456
PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false,
457
));
458
459
return id(new PHUIBoxView())
460
->appendChild($view)
461
->addPadding(PHUI::PADDING_LARGE);
462
}
463
464
}
465
466