Page MenuHomePhabricator

Extension data: Optionally include example installation + example usage.
Closed, DeclinedPublic

Description

Author: nickpj

Description:
It could be handy for users and for testers if extensions could optionally include an example of the minimal additions required to LocalSettings.php for installing them, and an example of how to use the extension (for Tag extensions or Parser Function extensions that alter wiki syntax). This is partly a problem of agreeing on a good convention, and partly a technical problem.

One possible way might be (using the ImageMap extension as an example) if two elements were added to the $wgExtensionCredits array:

$wgExtensionCredits['parserhook']['ImageMap'] = array(

>ImageMap
'svn-date' => '$LastChangedDate: 2008-05-06 21:59:58 +1000 (Tue, 06 May 2008) $',
'svn-revision' => '$LastChangedRevision: 34306 $',
'author'         => 'Tim Starling',
'url'            => 'http://www.mediawiki.org/wiki/Extension:ImageMap',
'description'    => 'Allows client-side clickable image maps using <nowiki><imagemap></nowiki> tag.',
'descriptionmsg' => 'imagemap_desc',

+ 'usage-example' => "<imagemap>\nImage:Framing_hammer_956.jpg|\nrect 1 1 299 218 [[Use a Hammer Safely]]\ndesc none\n</imagemap>",
+ 'basic-install' => 'require_once( "$IP/extensions/ImageMap/ImageMap.php" );',

);

... or alternatively, put all of this configuration information into a standard INI file name - e.g. "config.ini" - as having a standard file name containing this type of data could be useful to testers, and for testing software, which could now know how to install and smoketest an extension. An example config.ini could be:

name = ImageMap
svn-date = "$LastChangedDate: 2008-05-06 21:59:58 +1000 (Tue, 06 May 2008) $"
svn-revision = "$LastChangedRevision: 34306 $"
author = "Tim Starling"
url = "http://www.mediawiki.org/wiki/Extension:ImageMap"
description = "Allows client-side clickable image maps using <nowiki><imagemap></nowiki> tag."
descriptionmsg = "imagemap_desc"
usage-example = "<imagemap>
Image:Framing_hammer_956.jpg|
rect 1 1 299 218 [[Use a Hammer Safely]]
desc none
</imagemap>"

basic-install = "require_once( \"$IP/extensions/ImageMap/ImageMap.php\" );"

... and then use a line like to give current behaviour:

$wgExtensionCredits['parserhook']['ImageMap'] = parse_ini_file( 'config.ini' );

Note - the main problem with trying to give current behaviour in the above line is a bug in older versions of PHP with parse_ini_file: I have observed that the parse_ini_file in PHP 5.1.2 does not properly handle escaped quotes (as in the "basic-install" line above), and neither do builds snaps.php.net builds from March 2007, but it works correctly with snaps.php.net builds now, so at some point between March 2007 and now this PHP bug was fixed.


Version: 1.13.x
Severity: enhancement

Details

Reference
bz14204

Event Timeline

bzimport raised the priority of this task from to Lowest.Nov 21 2014, 10:08 PM
bzimport set Reference to bz14204.
bzimport added a subscriber: Unknown Object (MLST).

nickpj wrote:

How about a nice README file?

A nice README is perfect for humans, but not so good for software (unless there's a standard format). So just to clarify, this is so software can say "okay, this extension can be installed like so, and to get the extension to do something, I do X". Potential applications are for testing, and perhaps for some sort of web interface to make basic extension installation and telling-if-it-is-working possible using a GUI rather than via the command line. Most of my waffle above is talking about possible implementation details, but I probably should have outlined the reasons first :-)

INI Files are ugly. And even if they were used, with how badly PHP works with them we'd be using a custom wfParseIni function rather than using PHP's built in.

XML files are nicer, and more extensible. But the PHP support again isn't that great. Though, there is a PHP.XPath project on SourceForge which has a great library which works in both PHP4 and PHP5 that allows XPath to be used with absolutely no extra builtin PHP dependencies.

But honestly we shouldn't be doing this kind of stuff with the aim of GUI installation. Installing by a GUI needs a whole hellovalotta more data than what wgExtensionCredits contains or is meant to contain. You need everything from dependencies on other Extensions, to Dependencies on PHP builtins. And various other info including exactly what configuration options you have, and exactly what type and what kind of input can be put into them.

Before you even think of extending into another format, ask yourself why it is you need another format.
The answer to that is likely "Because I would need to load the Extension's file to get that info and that would install the extension".
That is what needs to be solved, not how extension credits are stored.

What language would we be building a GUI? PHP. Command line stuff would also be PHP. Basically there's little reason for anything other than PHP to be used for anything like installation or anything related to extensions. So what we need is not another format that is hard to make use of in PHP, it's a way to get extension data without actually loading the internals of the extension.

Something I've been pitching for awhile is a builtin Extension class, and a set of things which would make installation of extensions something more builtin.

<pre><?php
class WikiCode extends Extension {

/**

  • Static info functions
	 */

static function getExtensionCredit( $credit, $lang = null ) {

		switch( $credit ) {
			case 'type':return 'parserhook';
			case 'name':return __CLASS__;
			case 'author':return array(
				'realname' => 'Daniel Friesen',
				'url' => 'http://wiki-tools.com/wiki/User:Dantman',
				'email' => 'dan_the_man@telus.net
			case 'version':return '1.0a';
			case 'svn-date':return '$LastChangedDate: 2008-05-06 21:59:58 +1000 (Tue, 06 May 2008) $';
			case 'svn-revision':return '$LastChangedRevision: 34306 $';
			case 'url':return 'http://wiki-tools.com/wiki/WikiCode';
			case 'description':
				require self::getExtensionFile( 'i18n.description.inc' );
				return $description[$lang];
				break;
		}

}

static function getExtensionMessagePrefixes() {

		return 'wikicode';

}

static function getExtensionFile( $file = null ) {

		static $eIP = dirname(__FILE__);
		if( isset($file) ) return "$eIP/$file";
		return $eIP;

}

/**

  • Config variables
	 */

var $cUseUncacheableFunctions = true;
var $cUseStringFunctions = true;
var $cUseRegex = true;

/**

  • Config limits
	 */

var $cLimitTitleQuery = 100; Limit to how many queries for titles may be made.
var $cLimitTimeChars = 6000;

var $cLimitStringSearch = 30;
var $cLimitStringReplace = 30;

var $cLimitStringPad = 100;
var $cLimitLoopCount = 100;
Total limit to how many loops may be made in a page.

/**

  • Instanced extension
	 */

function __construct() {

		global $wgAutoloadClasses, $wgHooks, $wgExtensionMessagesFiles;
		$eIP = self::getExtensionFile();
		
		$wgAutoloadClasses['ExprError']  = "$eIP/Expr.php";
		$wgAutoloadClasses['ExprParser'] = "$eIP/Expr.php";
		// Create autoloads for the individual WikiCode groups
		foreach( array(
			'Logic', 'Loop', 'Page', 'Math', 'String', 'Time', 'Error',
			'Limiter'
		) as $group ) {
			$wgAutoloadClasses[__CLASS__.'_'.$group"] = "$eIP/FunctionClasses/{$group}.php";
		}
		
		$wgHooks['ParserGetHooks'][] = __CLASS__.'::SetupParserFunctionsForParser';
		
		$wgExtensionMessagesFiles[__CLASS__] = "$eIP/i18n.inc";
		
		return true; // Extension OK

}

function SetupParserFunctionsForParser( $parser ) {

		$parser->setFunctionHook( 'fi', array( __CLASS__.'_Logic', 'ifHook' ) );
		return true;

}

function SetupLanguageMagic( &$magicWords, $langCode ) {

  1. English is used as a fallback, and the English synonyms are
  2. used if a translation has not been provided for a given word
		$localWords = array();
		require self::getExtensionFile( 'i18n.magic.inc' );
		foreach( $words['en'] as $key => $value ) {
			if( isset($localWords[$key]) && is_array($localWords[$key]) ) {
				$localWords[$key] = array_merge( (array) $localWords[$key], (array) $value );
			} else {
				$localWords[$key] = (array) $value;
			}
		}
		if( $langCode != 'en' && isset($words[$langCode]) ) {
			foreach( $words[$langCode] as $key => $value ) {
				$localWords[$key] = array_merge( (array) $localWords[$key], (array) $value );
			}
		}
		foreach( $localWords as $word => $trans ) {
			$magic = $trans;
			array_unshift( $magic, 0 );
			$magicWords[$word] = $magic;
		}
		return true;

}
}</pre>

That would be stored in WikiCode/Extension.inc
Say for some reason you stored your extension in /var/mediawiki/extensions
And you also used my 'wiki-tools' subdirectory.
Then to load the extension, you would be using:
$wgExtensionPaths[] = '/var/mediawiki/extensions';
$wgExtensionPaths[] = '/var/mediawiki/extensions/wiki-tools';
$wgLoadExtensions['WikiCode'] = array(
'LimitTitleQuery' => 250
);

MediaWiki would load the extension when it is ready to. Additionally the $cLimitTitleQuery config there would be set to 250.
If you didn't notice it, the __construct() is basically equivalent to the $wgExtensionFunctions. And there are a few of my other proposals in there, extension message prefixes, and a hook for loading parserfunctions into the parser.
In short you could even track down a list of their extensions and the info, by grabbing the list of directories inside of the $wgExtensionPaths paths (Each being a name), and then checking if any contain a Extension.inc file, and then including that and calling $name::getExtensionCredit for any piece of info you need.
So static calls are info about the extension (Loading the file doesn't need to mean installation), and an instance is an installed extension. On a side note useful extension functions can also be programmed into the Extension class.