From a21cc4597c21d8216dc583f63397f035f1f83343 Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Fri, 27 Mar 2015 17:10:31 -0400 Subject: [PATCH] API: Add license info to API help output Which also involves some fun magic to figure out which extension a module belongs to. Bug: T93994 Change-Id: I236f573d79a5c683ae5714fa311f422c1c147cec --- includes/api/ApiBase.php | 92 +++++++++++++++++++ includes/api/ApiHelp.php | 62 +++++++++---- includes/api/ApiParamInfo.php | 18 ++++ includes/api/i18n/en.json | 5 + includes/api/i18n/qqq.json | 5 + resources/src/mediawiki/mediawiki.apihelp.css | 4 + 6 files changed, 170 insertions(+), 16 deletions(-) diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index 74e51c8838..6e289dcc4f 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -98,12 +98,17 @@ abstract class ApiBase extends ContextSource { */ const GET_VALUES_FOR_HELP = 1; + /** @var array Maps extension paths to info arrays */ + private static $extensionInfo = null; + /** @var ApiMain */ private $mMainModule; /** @var string */ private $mModuleName, $mModulePrefix; private $mSlaveDB = null; private $mParamCache = array(); + /** @var array|null|bool */ + private $mModuleSource = false; /** * @param ApiMain $mainModule @@ -2184,6 +2189,93 @@ abstract class ApiBase extends ContextSource { return $flags; } + /** + * Returns information about the source of this module, if known + * + * Returned array is an array with the following keys: + * - path: Install path + * - name: Extension name, or "MediaWiki" for core + * - namemsg: (optional) i18n message key for a display name + * - license-name: (optional) Name of license + * + * @return array|null + */ + protected function getModuleSourceInfo() { + global $IP; + + if ( $this->mModuleSource !== false ) { + return $this->mModuleSource; + } + + // First, try to find where the module comes from... + $rClass = new ReflectionClass( $this ); + $path = $rClass->getFileName(); + if ( !$path ) { + // No path known? + $this->mModuleSource = null; + return null; + } + $path = realpath( $path ) ?: $path; + + // Build map of extension directories to extension info + if ( self::$extensionInfo === null ) { + self::$extensionInfo = array( + realpath( __DIR__ ) ?: __DIR__ => array( + 'path' => $IP, + 'name' => 'MediaWiki', + 'license-name' => 'GPL-2.0+', + ), + realpath( "$IP/extensions" ) ?: "$IP/extensions" => null, + ); + $keep = array( + 'path' => null, + 'name' => null, + 'namemsg' => null, + 'license-name' => null, + ); + foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $group ) { + foreach ( $group as $ext ) { + if ( !isset( $ext['path'] ) || !isset( $ext['name'] ) ) { + // This shouldn't happen, but does anyway. + continue; + } + + $extpath = $ext['path']; + if ( !is_dir( $extpath ) ) { + $extpath = dirname( $extpath ); + } + self::$extensionInfo[realpath( $extpath ) ?: $extpath] = + array_intersect_key( $ext, $keep ); + } + } + foreach ( ExtensionRegistry::getInstance()->getAllThings() as $ext ) { + $extpath = $ext['path']; + if ( !is_dir( $extpath ) ) { + $extpath = dirname( $extpath ); + } + self::$extensionInfo[realpath( $extpath ) ?: $extpath] = + array_intersect_key( $ext, $keep ); + } + } + + // Now traverse parent directories until we find a match or run out of + // parents. + do { + if ( array_key_exists( $path, self::$extensionInfo ) ) { + // Found it! + $this->mModuleSource = self::$extensionInfo[$path]; + return $this->mModuleSource; + } + + $oldpath = $path; + $path = dirname( $path ); + } while ( $path !== $oldpath ); + + // No idea what extension this might be. + $this->mModuleSource = null; + return null; + } + /** * Called from ApiHelp before the pieces are joined together and returned. * diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php index dd05f45f46..d2d5e7c395 100644 --- a/includes/api/ApiHelp.php +++ b/includes/api/ApiHelp.php @@ -277,25 +277,55 @@ class ApiHelp extends ApiBase { ); } - $flags = $module->getHelpFlags(); - if ( $flags ) { - $help['flags'] .= Html::openElement( 'div', - array( 'class' => 'apihelp-block apihelp-flags' ) ); - $msg = $context->msg( 'api-help-flags' ); - if ( !$msg->isDisabled() ) { - $help['flags'] .= self::wrap( - $msg->numParams( count( $flags ) ), 'apihelp-block-head', 'div' - ); + $help['flags'] .= Html::openElement( 'div', + array( 'class' => 'apihelp-block apihelp-flags' ) ); + $msg = $context->msg( 'api-help-flags' ); + if ( !$msg->isDisabled() ) { + $help['flags'] .= self::wrap( + $msg->numParams( count( $flags ) ), 'apihelp-block-head', 'div' + ); + } + $help['flags'] .= Html::openElement( 'ul' ); + foreach ( $module->getHelpFlags() as $flag ) { + $help['flags'] .= Html::rawElement( 'li', null, + self::wrap( $context->msg( "api-help-flag-$flag" ), "apihelp-flag-$flag" ) + ); + } + $sourceInfo = $module->getModuleSourceInfo(); + if ( $sourceInfo ) { + if ( isset( $sourceInfo['namemsg'] ) ) { + $extname = $context->msg( $sourceInfo['namemsg'] )->text(); + } else { + $extname = $sourceInfo['name']; } - $help['flags'] .= Html::openElement( 'ul' ); - foreach ( $flags as $flag ) { - $help['flags'] .= Html::rawElement( 'li', null, - self::wrap( $context->msg( "api-help-flag-$flag" ), "apihelp-flag-$flag" ) - ); + $help['flags'] .= Html::rawElement( 'li', null, + self::wrap( + $context->msg( 'api-help-source', $extname, $sourceInfo['name'] ), + 'apihelp-source' + ) + ); + + $link = SpecialPage::getTitleFor( 'Version', 'License/' . $sourceInfo['name'] ); + if ( isset( $sourceInfo['license-name'] ) ) { + $msg = $context->msg( 'api-help-license', $link, $sourceInfo['license-name'] ); + } elseif ( SpecialVersion::getExtLicenseFileName( dirname( $sourceInfo['path'] ) ) ) { + $msg = $context->msg( 'api-help-license-noname', $link ); + } else { + $msg = $context->msg( 'api-help-license-unknown' ); } - $help['flags'] .= Html::closeElement( 'ul' ); - $help['flags'] .= Html::closeElement( 'div' ); + $help['flags'] .= Html::rawElement( 'li', null, + self::wrap( $msg, 'apihelp-license' ) + ); + } else { + $help['flags'] .= Html::rawElement( 'li', null, + self::wrap( $context->msg( 'api-help-source-unknown' ), 'apihelp-source' ) + ); + $help['flags'] .= Html::rawElement( 'li', null, + self::wrap( $context->msg( 'api-help-license-unknown' ), 'apihelp-license' ) + ); } + $help['flags'] .= Html::closeElement( 'ul' ); + $help['flags'] .= Html::closeElement( 'div' ); foreach ( $module->getFinalDescription() as $msg ) { $msg->setContext( $context ); diff --git a/includes/api/ApiParamInfo.php b/includes/api/ApiParamInfo.php index f0a5daf0f0..bb4967b174 100644 --- a/includes/api/ApiParamInfo.php +++ b/includes/api/ApiParamInfo.php @@ -195,6 +195,24 @@ class ApiParamInfo extends ApiBase { } $ret['prefix'] = $module->getModulePrefix(); + $sourceInfo = $module->getModuleSourceInfo(); + if ( $sourceInfo ) { + $ret['source'] = $sourceInfo['name']; + if ( isset( $sourceInfo['namemsg'] ) ) { + $ret['sourcename'] = $this->context->msg( $sourceInfo['namemsg'] )->text(); + } else { + $ret['sourcename'] = $ret['source']; + } + + $link = SpecialPage::getTitleFor( 'Version', 'License/' . $sourceInfo['name'] )->getFullUrl(); + if ( isset( $sourceInfo['license-name'] ) ) { + $ret['licensetag'] = $sourceInfo['license-name']; + $ret['licenselink'] = (string)$link; + } elseif ( SpecialVersion::getExtLicenseFileName( dirname( $sourceInfo['path'] ) ) ) { + $ret['licenselink'] = (string)$link; + } + } + $this->formatHelpMessages( $ret, 'description', $module->getFinalDescription() ); foreach ( $module->getHelpFlags() as $flag ) { diff --git a/includes/api/i18n/en.json b/includes/api/i18n/en.json index 9d0663c448..d1d408fbf4 100644 --- a/includes/api/i18n/en.json +++ b/includes/api/i18n/en.json @@ -1121,6 +1121,11 @@ "api-help-flag-writerights": "This module requires write rights.", "api-help-flag-mustbeposted": "This module only accepts POST requests.", "api-help-flag-generator": "This module can be used as a generator.", + "api-help-source": "Source: $1", + "api-help-source-unknown": "Source: unknown", + "api-help-license": "License: [[$1|$2]]", + "api-help-license-noname": "License: [[$1|See link]]", + "api-help-license-unknown": "License: unknown", "api-help-help-urls": "", "api-help-parameters": "{{PLURAL:$1|Parameter|Parameters}}:", "api-help-param-deprecated": "Deprecated.", diff --git a/includes/api/i18n/qqq.json b/includes/api/i18n/qqq.json index 05b77222c5..5ef5b3dac3 100644 --- a/includes/api/i18n/qqq.json +++ b/includes/api/i18n/qqq.json @@ -1023,6 +1023,11 @@ "api-help-flag-writerights": "Flag displayed for an API module that requires write rights", "api-help-flag-mustbeposted": "Flag displayed for an API module that only accepts POST requests", "api-help-flag-generator": "Flag displayed for an API module that can be used as a generator", + "api-help-source": "Displayed in the flags box to indicate the source of an API module.\n\nParameters:\n* $1 - Possibly-localised extension name, or \"MediaWiki\" if it's a core module\n* $2 - Non-localised extension name.\n\nSee also:\n* {{msg-mw|api-help-source-unknown}}", + "api-help-source-unknown": "Displayed in the flags box to indicate that the source of an API module is not known.\n\nSee also:\n* {{msg-mw|api-help-source}}", + "api-help-license": "Displayed in the flags box to indicate the license of an API module.\n\nParameters:\n* $1 - Page to link to display the full license text\n* $2 - Display text for the link\n\nSee also:\n* {{msg-mw|api-help-license-noname}}\n* {{msg-mw|api-help-license-unknown}}", + "api-help-license-noname": "Displayed in the flags box to indicate the license of an API module, when the tag for the license is not known.\n\nParameters:\n* $1 - Page to link to display the full license text\n\nSee also:\n* {{msg-mw|api-help-license}}\n* {{msg-mw|api-help-license-unknown}}", + "api-help-license-unknown": "Displayed in the flags box to indicate that the license of the API module is not known.\n\nSee also:\n* {{msg-mw|api-help-license}}\n* {{msg-mw|api-help-license-noname}}", "api-help-help-urls": "{{optional}} Label for the API help urls section\n\nParameters:\n* $1 - Number of urls to be displayed", "api-help-parameters": "Label for the API help parameters section\n\nParameters:\n* $1 - Number of parameters to be displayed\n{{Identical|Parameter}}", "api-help-param-deprecated": "Displayed in the API help for any deprecated parameter\n{{Identical|Deprecated}}", diff --git a/resources/src/mediawiki/mediawiki.apihelp.css b/resources/src/mediawiki/mediawiki.apihelp.css index d1272323ee..7d7b413a10 100644 --- a/resources/src/mediawiki/mediawiki.apihelp.css +++ b/resources/src/mediawiki/mediawiki.apihelp.css @@ -29,6 +29,10 @@ div.apihelp-linktrail { color: red; } +.apihelp-unknown { + color: #888; +} + .apihelp-empty { color: #888; } -- 2.20.1