From e9fd126b9a43da744a593dbce5018b4190e10218 Mon Sep 17 00:00:00 2001 From: Bryan Tong Minh Date: Thu, 28 Oct 2010 19:20:21 +0000 Subject: [PATCH] (bug 25648) API discovery information has been added as RSD link in page and by providing an API module action=rsd. Added hook ApiRsdServiceApis for extensions to add their own service to the services list. Patch by Brion Vibber and Bryan Tong Minh. --- RELEASE-NOTES | 4 + docs/hooks.txt | 7 ++ includes/AutoLoader.php | 1 + includes/Skin.php | 10 +++ includes/api/ApiMain.php | 1 + includes/api/ApiRsd.php | 183 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 206 insertions(+) create mode 100644 includes/api/ApiRsd.php diff --git a/RELEASE-NOTES b/RELEASE-NOTES index eaf0f7c804..7d33ed973b 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -455,6 +455,10 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN id, try the parser cache, and save it to it if necessary * (bug 25463) Export header should not be shown if no pages were requested, to reduce confusion +* (bug 25648) API discovery information has been added as RSD link in page + and by providing an API module action=rsd. Added hook + ApiRsdServiceApis for extensions to add their own service to the services + list. === Languages updated in 1.17 === diff --git a/docs/hooks.txt b/docs/hooks.txt index 05d1521594..26e3c36b6c 100644 --- a/docs/hooks.txt +++ b/docs/hooks.txt @@ -362,6 +362,13 @@ is the User object. In the hook, just add your callback to the $tokenFunctions array and return true (returning false makes no sense) $tokenFunctions: array(action => callback) +'ApiRsdServiceApis': Add or remove APIs from the RSD services list. +Each service should have its own entry in the $apis array and have a +unique name, passed as key for the array that represents the service data. +In this data array, the key-value-pair identified by the apiLink key is +required. +&$apis: array of services + 'ArticleAfterFetchContent': after fetching content of an article from the database $article: the article (object) being loaded from the database diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 4519fd090b..9dca46d98d 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -297,6 +297,7 @@ $wgAutoloadLocalClasses = array( 'ApiPatrol' => 'includes/api/ApiPatrol.php', 'ApiProtect' => 'includes/api/ApiProtect.php', 'ApiPurge' => 'includes/api/ApiPurge.php', + 'ApiRsd' => 'includes/api/ApiRsd.php', 'ApiQuery' => 'includes/api/ApiQuery.php', 'ApiQueryAllCategories' => 'includes/api/ApiQueryAllCategories.php', 'ApiQueryAllimages' => 'includes/api/ApiQueryAllimages.php', diff --git a/includes/Skin.php b/includes/Skin.php index dc53517644..0b44a6768b 100644 --- a/includes/Skin.php +++ b/includes/Skin.php @@ -216,6 +216,16 @@ class Skin extends Linker { 'title' => wfMsgForContent( 'opensearch-desc' ), ) ); + # Real Simple Discovery link, provides auto-discovery information + # for the MediaWiki API (and potentially additional custom API + # support such as WordPress or Twitter-compatible APIs for a + # blogging extension, etc) + $out->addLink( array( + 'rel' => 'EditURI', + 'type' => 'application/rsd+xml', + 'href' => wfExpandUrl( wfAppendQuery( wfScript( 'api' ), array( 'action' => 'rsd' ) ) ), + ) ); + $this->addMetadataLinks( $out ); $this->mRevisionId = $out->mRevisionId; diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index 19ad59a688..2f21201f7e 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -63,6 +63,7 @@ class ApiMain extends ApiBase { 'feedwatchlist' => 'ApiFeedWatchlist', 'help' => 'ApiHelp', 'paraminfo' => 'ApiParamInfo', + 'rsd' => 'ApiRsd', // Write modules 'purge' => 'ApiPurge', diff --git a/includes/api/ApiRsd.php b/includes/api/ApiRsd.php new file mode 100644 index 0000000000..2c81e979ae --- /dev/null +++ b/includes/api/ApiRsd.php @@ -0,0 +1,183 @@ +getResult(); + + + $result->addValue( null, 'version', '1.0' ); + $result->addValue( null, 'xmlns', 'http://archipelago.phrasewise.com/rsd' ); + + $service = array( + 'engineName' => array( + '*' => 'MediaWiki' + ), + 'engineLink' => array( + '*' => 'http://www.mediawiki.org/' + ), + 'apis' => $this->formatRsdApiList() + ); + + $result->setIndexedTagName( $service['apis'], 'api' ); + + $result->addValue( null, 'service', $service ); + } + + public function getCustomPrinter() { + return new ApiFormatXmlRsd( $this->getMain(), 'xml' ); + } + + public function getAllowedParams() { + return array(); + } + + public function getParamDescription() { + return array(); + } + + public function getDescription() { + return 'Export an RSD schema'; + } + + protected function getExamples() { + return array( + 'api.php?action=rsd' + ); + } + + /** + * Builds an internal list of APIs to expose information about. + * Normally this only lists the MediaWiki API, with its base URL, + * link to documentation, and a marker as to available authentication + * (to aid in OAuth client apps switching to support in the future). + * + * Extensions can expose other APIs, such as WordPress or Twitter- + * compatible APIs, by hooking 'ApiRsdServiceApis' and adding more + * elements to the array. + * + * See http://cyber.law.harvard.edu/blogs/gems/tech/rsd.html for + * the base RSD spec, and check WordPress and StatusNet sites for + * in-production examples listing several blogging and micrblogging + * APIs. + * + * @return array + */ + protected function getRsdApiList() { + $apis = array( + 'MediaWiki' => array( + // The API link is required for all RSD API entries. + 'apiLink' => wfExpandUrl( wfScript( 'api' ) ), + + // Docs link is optional, but recommended. + 'docs' => 'http://mediawiki.org/wiki/API', + + // Some APIs may need a blog ID, but it may be left blank. + 'blogID' => '', + + // Additional settings are optional. + 'settings' => array( + // Change this to true in the future as an aid to + // machine discovery of OAuth for API access. + 'OAuth' => false, + ) + ), + ); + wfRunHooks( 'ApiRsdServiceApis', array( &$apis ) ); + return $apis; + } + + /** + * Formats the internal list of exposed APIs into an array suitable + * to pass to the API's XML formatter. + * + * @return array + */ + protected function formatRsdApiList() { + $apis = $this->getRsdApiList(); + + $outputData = array(); + foreach ( $apis as $name => $info ) { + $data = array( + 'name' => $name, + 'preferred' => wfBoolToStr( $name == 'MediaWiki' ), + 'apiLink' => $info['apiLink'], + 'blogID' => isset( $info['blogID'] ) ? $info['blogID'] : '' + ); + if ( isset( $info['docs'] ) ) { + $data['settings']['docs'] = array( + '*' => $info['docs'], + ); + } + if ( isset( $info['settings'] ) ) { + foreach ( $info['settings'] as $setting => $val ) { + if ( is_bool( $val ) ) { + $xmlVal = wfBoolToStr( $val ); + } else { + $xmlVal = $val; + } + $data['settings'][] = array( + 'name' => $setting, + '*' => $xmlVal, + ); + } + } + if ( isset( $data['settings'] ) ) { + $data['settings']['_element'] = 'setting'; + } + $outputData[] = $data; + } + return $outputData; + } + public function getVersion() { + return __CLASS__ . ': $Id$'; + } +} + +class ApiFormatXmlRsd extends ApiFormatXml { + public function __construct( $main, $format ) { + parent::__construct( $main, $format ); + $this->setRootElement( 'rsd' ); + } + + public function getMimeType() { + return 'application/rsd+xml'; + } +} -- 2.20.1