Path: blob/master/src/applications/conduit/method/ConduitAPIMethod.php
12256 views
<?php12/**3* @task info Method Information4* @task status Method Status5* @task pager Paging Results6*/7abstract class ConduitAPIMethod8extends Phobject9implements PhabricatorPolicyInterface {1011private $viewer;1213const METHOD_STATUS_STABLE = 'stable';14const METHOD_STATUS_UNSTABLE = 'unstable';15const METHOD_STATUS_DEPRECATED = 'deprecated';16const METHOD_STATUS_FROZEN = 'frozen';1718const SCOPE_NEVER = 'scope.never';19const SCOPE_ALWAYS = 'scope.always';2021/**22* Get a short, human-readable text summary of the method.23*24* @return string Short summary of method.25* @task info26*/27public function getMethodSummary() {28return $this->getMethodDescription();29}303132/**33* Get a detailed description of the method.34*35* This method should return remarkup.36*37* @return string Detailed description of the method.38* @task info39*/40abstract public function getMethodDescription();4142final public function getDocumentationPages(PhabricatorUser $viewer) {43$pages = $this->newDocumentationPages($viewer);44return $pages;45}4647protected function newDocumentationPages(PhabricatorUser $viewer) {48return array();49}5051final protected function newDocumentationPage(PhabricatorUser $viewer) {52return id(new ConduitAPIDocumentationPage())53->setIconIcon('fa-chevron-right');54}5556final protected function newDocumentationBoxPage(57PhabricatorUser $viewer,58$title,59$content) {6061$box_view = id(new PHUIObjectBoxView())62->setHeaderText($title)63->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)64->setTable($content);6566return $this->newDocumentationPage($viewer)67->setName($title)68->setContent($box_view);69}7071abstract protected function defineParamTypes();72abstract protected function defineReturnType();7374protected function defineErrorTypes() {75return array();76}7778abstract protected function execute(ConduitAPIRequest $request);7980public function isInternalAPI() {81return false;82}8384public function getParamTypes() {85$types = $this->defineParamTypes();8687$query = $this->newQueryObject();88if ($query) {89$types['order'] = 'optional order';90$types += $this->getPagerParamTypes();91}9293return $types;94}9596public function getReturnType() {97return $this->defineReturnType();98}99100public function getErrorTypes() {101return $this->defineErrorTypes();102}103104/**105* This is mostly for compatibility with106* @{class:PhabricatorCursorPagedPolicyAwareQuery}.107*/108public function getID() {109return $this->getAPIMethodName();110}111112/**113* Get the status for this method (e.g., stable, unstable or deprecated).114* Should return a METHOD_STATUS_* constant. By default, methods are115* "stable".116*117* @return const METHOD_STATUS_* constant.118* @task status119*/120public function getMethodStatus() {121return self::METHOD_STATUS_STABLE;122}123124/**125* Optional description to supplement the method status. In particular, if126* a method is deprecated, you can return a string here describing the reason127* for deprecation and stable alternatives.128*129* @return string|null Description of the method status, if available.130* @task status131*/132public function getMethodStatusDescription() {133return null;134}135136public function getErrorDescription($error_code) {137return idx($this->getErrorTypes(), $error_code, pht('Unknown Error'));138}139140public function getRequiredScope() {141return self::SCOPE_NEVER;142}143144public function executeMethod(ConduitAPIRequest $request) {145$this->setViewer($request->getUser());146147$client = $this->newConduitCallProxyClient($request);148if ($client) {149// We're proxying, so just make an intracluster call.150return $client->callMethodSynchronous(151$this->getAPIMethodName(),152$request->getAllParameters());153}154155return $this->execute($request);156}157158protected function newConduitCallProxyClient(ConduitAPIRequest $request) {159return null;160}161162abstract public function getAPIMethodName();163164/**165* Return a key which sorts methods by application name, then method status,166* then method name.167*/168public function getSortOrder() {169$name = $this->getAPIMethodName();170171$map = array(172self::METHOD_STATUS_STABLE => 0,173self::METHOD_STATUS_UNSTABLE => 1,174self::METHOD_STATUS_DEPRECATED => 2,175);176$ord = idx($map, $this->getMethodStatus(), 0);177178list($head, $tail) = explode('.', $name, 2);179180return "{$head}.{$ord}.{$tail}";181}182183public static function getMethodStatusMap() {184$map = array(185self::METHOD_STATUS_STABLE => pht('Stable'),186self::METHOD_STATUS_UNSTABLE => pht('Unstable'),187self::METHOD_STATUS_DEPRECATED => pht('Deprecated'),188);189190return $map;191}192193public function getApplicationName() {194return head(explode('.', $this->getAPIMethodName(), 2));195}196197public static function loadAllConduitMethods() {198return self::newClassMapQuery()->execute();199}200201private static function newClassMapQuery() {202return id(new PhutilClassMapQuery())203->setAncestorClass(__CLASS__)204->setUniqueMethod('getAPIMethodName');205}206207public static function getConduitMethod($method_name) {208return id(new PhabricatorCachedClassMapQuery())209->setClassMapQuery(self::newClassMapQuery())210->setMapKeyMethod('getAPIMethodName')211->loadClass($method_name);212}213214public function shouldRequireAuthentication() {215return true;216}217218public function shouldAllowPublic() {219return false;220}221222public function shouldAllowUnguardedWrites() {223return false;224}225226227/**228* Optionally, return a @{class:PhabricatorApplication} which this call is229* part of. The call will be disabled when the application is uninstalled.230*231* @return PhabricatorApplication|null Related application.232*/233public function getApplication() {234return null;235}236237protected function formatStringConstants($constants) {238foreach ($constants as $key => $value) {239$constants[$key] = '"'.$value.'"';240}241$constants = implode(', ', $constants);242return 'string-constant<'.$constants.'>';243}244245public static function getParameterMetadataKey($key) {246if (strncmp($key, 'api.', 4) === 0) {247// All keys passed beginning with "api." are always metadata keys.248return substr($key, 4);249} else {250switch ($key) {251// These are real keys which always belong to request metadata.252case 'access_token':253case 'scope':254case 'output':255256// This is not a real metadata key; it is included here only to257// prevent Conduit methods from defining it.258case '__conduit__':259260// This is prevented globally as a blanket defense against OAuth261// redirection attacks. It is included here to stop Conduit methods262// from defining it.263case 'code':264265// This is not a real metadata key, but the presence of this266// parameter triggers an alternate request decoding pathway.267case 'params':268return $key;269}270}271272return null;273}274275final public function setViewer(PhabricatorUser $viewer) {276$this->viewer = $viewer;277return $this;278}279280final public function getViewer() {281return $this->viewer;282}283284/* -( Paging Results )----------------------------------------------------- */285286287/**288* @task pager289*/290protected function getPagerParamTypes() {291return array(292'before' => 'optional string',293'after' => 'optional string',294'limit' => 'optional int (default = 100)',295);296}297298299/**300* @task pager301*/302protected function newPager(ConduitAPIRequest $request) {303$limit = $request->getValue('limit', 100);304$limit = min(1000, $limit);305$limit = max(1, $limit);306307$pager = id(new AphrontCursorPagerView())308->setPageSize($limit);309310$before_id = $request->getValue('before');311if ($before_id !== null) {312$pager->setBeforeID($before_id);313}314315$after_id = $request->getValue('after');316if ($after_id !== null) {317$pager->setAfterID($after_id);318}319320return $pager;321}322323324/**325* @task pager326*/327protected function addPagerResults(328array $results,329AphrontCursorPagerView $pager) {330331$results['cursor'] = array(332'limit' => $pager->getPageSize(),333'after' => $pager->getNextPageID(),334'before' => $pager->getPrevPageID(),335);336337return $results;338}339340341/* -( Implementing Query Methods )----------------------------------------- */342343344public function newQueryObject() {345return null;346}347348349protected function newQueryForRequest(ConduitAPIRequest $request) {350$query = $this->newQueryObject();351352if (!$query) {353throw new Exception(354pht(355'You can not call newQueryFromRequest() in this method ("%s") '.356'because it does not implement newQueryObject().',357get_class($this)));358}359360if (!($query instanceof PhabricatorCursorPagedPolicyAwareQuery)) {361throw new Exception(362pht(363'Call to method newQueryObject() did not return an object of class '.364'"%s".',365'PhabricatorCursorPagedPolicyAwareQuery'));366}367368$query->setViewer($request->getUser());369370$order = $request->getValue('order');371if ($order !== null) {372if (is_scalar($order)) {373$query->setOrder($order);374} else {375$query->setOrderVector($order);376}377}378379return $query;380}381382383/* -( PhabricatorPolicyInterface )----------------------------------------- */384385386public function getPHID() {387return null;388}389390public function getCapabilities() {391return array(392PhabricatorPolicyCapability::CAN_VIEW,393);394}395396public function getPolicy($capability) {397// Application methods get application visibility; other methods get open398// visibility.399400$application = $this->getApplication();401if ($application) {402return $application->getPolicy($capability);403}404405return PhabricatorPolicies::getMostOpenPolicy();406}407408public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {409if (!$this->shouldRequireAuthentication()) {410// Make unauthenticated methods universally visible.411return true;412}413414return false;415}416417protected function hasApplicationCapability(418$capability,419PhabricatorUser $viewer) {420421$application = $this->getApplication();422423if (!$application) {424return false;425}426427return PhabricatorPolicyFilter::hasCapability(428$viewer,429$application,430$capability);431}432433protected function requireApplicationCapability(434$capability,435PhabricatorUser $viewer) {436437$application = $this->getApplication();438if (!$application) {439return;440}441442PhabricatorPolicyFilter::requireCapability(443$viewer,444$this->getApplication(),445$capability);446}447448final protected function newRemarkupDocumentationView($remarkup) {449$viewer = $this->getViewer();450451$view = new PHUIRemarkupView($viewer, $remarkup);452453$view->setRemarkupOptions(454array(455PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false,456));457458return id(new PHUIBoxView())459->appendChild($view)460->addPadding(PHUI::PADDING_LARGE);461}462463}464465466