Manual talk:Extension registration

Jump to navigation Jump to search

About this board

How to instantiate a "Foo" object right after wfLoadExtension('Foo')?

6
Maiden taiwan (talkcontribs)

I have an old extension that basically does this in LocalSettings.php:

require_once('extensions/Foo.php');

Foo::DoSomething();

When I convert it to use wfLoadExtension(), the code becomes:

wfLoadExtension('Foo');

Foo::DoSomething();

The second line fails with the error:

PHP Fatal error:  Uncaught Error: Class 'Foo' not found in <path>/w/LocalSettings.php

It looks like wfLoadExtension('Foo') does not make the class Foo available in time. (The extension.js file point properly to the file containing the Foo class.)

Is there a workaround? The extension must run Foo::DoSomething() in global scope.

Jdforrester (WMF) (talkcontribs)
Maiden taiwan (talkcontribs)

Thanks so much for the quick suggestion! I see, however, that the example (ArticleRatings) uses the "global" keyword in front of the variable it sets. For various reasons, it's better if my extension can run code in global scope without using the "global" keyword.

Some context: I have written an extension that makes it super-easy to run and manage a multi-wiki farm from a single MediaWiki codebase. The extension lets you define separate "LocalSettings" config files for each site or for collections of sites. The extension must load these config files in global scope (since they set global variables). I'd rather not force authors to write "global" in the mini config files in front of every variable, so they can directly copy code from LocalSettings.php (or from mediawiki.org docs) into the mini config files. And if they ever overlook a "global", it's a subtle bug waiting to happen.

Is there a way to manage this kind of loading with wfLoadExtension/extension.json?

Legoktm (talkcontribs)

No, it's really not possible. The main issue I think you'd run into is that with extension.json, the load order of configuration has changed. All configuration is expected to be in place before extensions are loaded and initialized in Setup.php. Trying to go back and then change some configuration stuff is unlikely to work.

Additionally, the requirement of executing extension code in global scope goes directly against the design principles of extension.json, which was to eliminate that global scope.

Jdforrester (WMF) (talkcontribs)

Why can't they set configuration in LocalSettings in the normal way (which are all implicitly global), and then you write a post-registration function that takes their config and injects it into your extension's config objects?

Maiden taiwan (talkcontribs)

Hmm... may I try to unpack the second half of your sentence with a specific example?

Suppose I'm running a wiki farm with two wikis called Wiki1 and Wiki2 that share a MediaWiki tree. Wiki1 simply uses LocalSettings.php for configuration. Wiki2 has a second config file (to be loaded by my extension) that overrides values in LocalSettings.

Suppose LocalSettings.php sets $wgFoobar = true, and Wiki2's config file consists of one line:

$wgFoobar = false;

that is intended to override the value in LocalSettings.

I know how to write a "post-registration function" for my extension, as you suggest, but what you do you mean that the function "takes their config and injects it into your extensions config objects"? I am not sure what a "config object" is. What specifically would be done with the config file for Wiki2 to run its code?

Thank you very much for your help!!!

Reply to "How to instantiate a "Foo" object right after wfLoadExtension('Foo')?"

How to make site-specific changes to an extension's configuration *Without* touching extension.js?

2
Otheus (talkcontribs)

There is also no documentation on the PHP code corresponding to the config section for version 2. The reader is left to experiment.

Samwilson (talkcontribs)

(I assume you mean 'extension.json'.)

You change extensions' config values the same way in version 2 of the extension.json schema, e.g. with $wgMyExtVarname = 'site-specific value'; in LocalSettings.php.

Reply to "How to make site-specific changes to an extension's configuration *Without* touching extension.js?"

Please confirm: mergeMessageFileList compatibility no longer necessary?

4
Summary by Lucas Werkmeister (WMDE)

still necessary as long as extension-list references PHP entry point rather than JSON file; once JSON file is used, PHP entry point can be removed completely

Lucas Werkmeister (WMDE) (talkcontribs)

The Migration for extension developers section has some boilerplate with a mostly empty PHP entry point, but keep[ing] i18n globals so mergeMessageFileList.php doesn't break. Plenty of extensions follow this approach, but as far as I can tell, by this point that’s just cargo culting: Legoktm added support for extension/skin.json files to that maintenance script four years ago. Can someone confirm that this understanding is correct, and I don’t need to bother keeping i18n globals when porting extensions to extension registration nowadays?

Legoktm (talkcontribs)

You still need to add them. mergeMessageFileList works on a hardcoded extension list (example), and until that is updated to point to extension.json instead of the PHP entry point, the i18n globals need to be kept since the point of the shim is to be a drop-in replacement with no config changes needed.

Lucas Werkmeister (WMDE) (talkcontribs)

Okay, but if I understand correctly it doesn’t need to be kept indefinitely? It looks like the workflow could be: convert to extension.json but keep shim in PHP entry point, update extension-list to point to extension.json, remove shim from PHP entry point (or even completely remove PHP entry point).

Jdforrester (WMF) (talkcontribs)

Correct.

We currently load eight repos via their PHP file (CirrusSearch, Collection, FlaggedRevs, LdapAuthentication, OpenStackManager, Translate, and Wikibase & WikibaseClient).

Most of the extensions in production don't have the backwards-compatibility shim any more (e.g. CodeEditor), or never did (e.g. 3D).

How can I translate the namespace “name” to a local language the right way?

5
Andreas Plank (talkcontribs)

Hej-hej,

I asked for translating the default namespace (at extension talk of UploadWizard), how is it possible to do it the right way? $wgNamespaceAliases doesn’t help that much, because I want the translated namespace to appear (in search and display) not the default defined from extension.json. Any proper solution? Thanks.

Legoktm (talkcontribs)

Namespaces should be translated in the extension localization file, see this example in the Scribunto extension. I don't know why no one has done that for UploadWizard yet.

Jdforrester (WMF) (talkcontribs)
Jdforrester (WMF) (talkcontribs)

Yes, you add the name to $wgNamespaceAliases in local config of the wiki in question. For Wikimedia, you'd add that to the major InitialiseSettings.php file.

The alias only helps for manual linking; the URL is re-written by MediaWiki on browse (https://commons.wikimedia.org/wiki/COM:Deletion_requests becomes https://commons.wikimedia.org/wiki/Commons:Deletion_requests, etc.), so I'm not sure how valuable this would be. In general, MediaWiki's support for multi-lingual wikis is patchy, and this is one of those areas. Note also that there's a risk of whatever name you pick being subject to clashes, either with other desired aliases or for actual namespaces in future.

Currently, Commons only has language aliases/shorthands in English and for three namespaces ('Museum:'/'Museum_talk:' for 'Institution:'/'Institution_talk:', and 'COM:' for Commons:"). We could add a "translation" for NS_CAMPAIGN, but as this hasn't been done before I think it'd be best if there was a community discussion on Commons about that first.

Andreas Plank (talkcontribs)

In MW 1.30 I did the following:

if (! defined ("NS_CAMPAIGN" )) {
  define("NS_CAMPAIGN",        460);
  define("NS_CAMPAIGN_TALK",   461);
}
# rename untranslated Campaign
switch ($wgLanguageCode) {
  case "de":
  case "de-formal":
  case "de-at":
  case "de-ch":
    $wgExtraNamespaces[NS_CAMPAIGN]      = "Medienkampagne";
    $wgExtraNamespaces[NS_CAMPAIGN_TALK] = "Medienkampagne_Diskussion";
    $wgNamespaceAliases['Campaign'] = NS_CAMPAIGN;
    $wgNamespaceAliases['Campaign_talk'] = NS_CAMPAIGN_TALK;
    break;
  // other cases 
  default:
    $wgExtraNamespaces[NS_CAMPAIGN]      = "Campaign";
    $wgExtraNamespaces[NS_CAMPAIGN_TALK] = "Campaign_talk";
  break;
}

#############################
# Extension: UploadWizard & Co
#############################
wfLoadExtension( 'EventLogging' );
wfLoadExtension( 'UploadWizard' );

and it works as I want it to:

  1. it uses Medienkampagne on a German Wiki as default
  2. but Campaign also works (and is led to Medienkampagne)

I don’t know if it is the right way but it works for me ;-)

Reply to "How can I translate the namespace “name” to a local language the right way?"

How to get extension version?

3
Planetenxin (talkcontribs)

With ExtensionRegistry::getInstance()->isLoaded( 'ExtensionB' ) one can check if an extension is loaded.

What would be the best way to get ExtensionB's version (as defined in extension.json?

Florianschmidtwelzow (talkcontribs)

If you want to do that (I don't think that this is really needed somewhere), you can use something like:

echo ExtensionRegistry::getInstance()->getAllThings()['GoogleLogin']['version'];

which will print the version of Extension:GoogleLogin.

However, it would be nice, if you would give some more information about what do you want to do. E.g. if you want to check, that a specific other extension is installed and has a specific version, you can (and probably use) the requirements/dependencies feature of extension registration.

213.61.254.67 (talkcontribs)

I can not get the extension infos.

I tried:

$geshiExists = defined('SyntaxHighlight_GeSHi');

$geshiExists = ExtensionRegistry::getInstance()->isLoaded( 'SyntaxHighlight_GeSHi');

$geshiExists = class_exists( 'GeSHi');

$geshiExists = class_exists( 'SyntaxHighlight');

$geshiExists = ExtensionRegistry::getInstance()->getAllThings()['SyntaxHighlight_GeSHi']['version'];

Reply to "How to get extension version?"

Howto change the group on Special:Specialpages in the json ?

1
Karima Rafes (talkcontribs)

I don't understand the classic method.

I suppose there is a new method via the json file.

Can you give an example to change the group of a special page and maybe update the doc on Mediawiki ?

Thanks

Reply to "Howto change the group on Special:Specialpages in the json ?"

Documentation on extension.json format and how to write a file

2
Brion VIBBER (talkcontribs)

Could use some. :) Seems to be essentially no documentation on the format, how to specify things, etc. Does everyone just copy-paste from existing files? :(

Brion VIBBER (talkcontribs)
Reply to "Documentation on extension.json format and how to write a file"
Kaldari (talkcontribs)

What's the registerExtension() function for? Registering stuff that can't be registered through the JSON file?

Legoktm (talkcontribs)

Can you give an example of what you mean?

Kaldari (talkcontribs)

Two extensions (Math and Spamblacklist) define a registerExtension() function that is never explicitly called. There are also patches in gerrit to add it to two other extensions (TimedMediaHandler and MobileFrontend). The function has no documentation anywhere. As best as I can tell it seems to be for registering globals that can't be migrated to extension.json.

Legoktm (talkcontribs)

Those are "callbacks", see the docs. Some of them, like the ones in WikiEditor were necessary because extension.json didn't support 2d arrays (it does now) and can probably be removed.

Kaldari (talkcontribs)

Why is this implemented in such a convoluted way? Couldn't we just use a regular hook?

Legoktm (talkcontribs)

It's somewhat intentionally convoluted, any instance of it indicates that extension.json is missing support for a feature or an extension is doing something wrong.

A hook isn't feasible due to the way these are called, plus we try to avoid as many dependencies upon the rest of MediaWiki in early initialization, including the Hooks class.

Kaldari (talkcontribs)

OK, I'm beginning to understand this. If people would just follow the coding conventions and document their functions, things wouldn't be so confusing :P

Would it be possible for you or someone else involved in extension registration to update Manual:Extension registration/Limitations? This seems like a pretty critical documentation page and it hasn't been kept up to date. I tried to make a few changes myself, but I don't know the Phab tickets for any of these issues. Thanks!

SPage (WMF) (talkcontribs)
  1. It's confusing that the example gives it a hook name but it isn't. I added a note to Manual:Extension registration#Customizing registration mentioning this.
  2. When does registration call the callback, is it immediately after registering this one extension or after registering all extensions? Some extensions try to do stuff based on the presence of other extensions, and then wind up being order-dependent.
  3. The section says "There are two features that can be used to support these cases". What's the second?

Answer here or update the docs, thanks

Reply to "registerExtension() function"

Documentation for how to register an extension

1
79.86.113.37 (talkcontribs)

Coming to this for the first time for writing my first extension, I don't see any guide as to how to write the code to register a new extension, only how to convert an old extension. Maybe it is obvious, but at least a simple example would help.

Reply to "Documentation for how to register an extension"

Comments (keys starting with @ in extension.json) not ignored in "SpecialPages" array

4
217.15.199.78 (talkcontribs)

Hello,

Special:Specialpages crashes (in SpecialPageFactory.php/getPage()/"$page = new $className;") if there are comments (like "@doc": "Something") under "SpecialPages" array in extension.json. It seems keys starting with "@" are not as filtered as this page suggests...

Legoktm (talkcontribs)

"@" is only ignored in the top level structure, or under the "config" key.

SPage (WMF) (talkcontribs)
Legoktm (talkcontribs)

Thanks. Re: Gather, that only works because ResourceLoader is ignoring the unknown parameter, I wouldn't depend upon it. The extension registry is still loading and caching that value on each request unlike "@" keys in top-level/config which are ignored in the parsing stage.

Reply to "Comments (keys starting with @ in extension.json) not ignored in "SpecialPages" array"