Page MenuHomePhabricator

How to make a good MediaWiki distribution?
Closed, InvalidPublic

Description

The BlueSpice project consists of a number of extensions/skins that augment MediaWiki. Besides the BlueSpice* extensions/skins there are several other extensions that are needed by or just installed together with BlueSpice.

  • Examples for extensions that are needed by BlueSpice: "Lockdown", "Echo", "RSS", ...
  • Examples for extensions that are usually installed together with BlueSpice: "MobileFrontend", "TitleKey", "NSFileRepo", "CategoryTree", "ImageMapEdit", "DynamicPageList", "LdapAuthentication" ...

At the moment we have a non public repo that holds an extension called "BlueSpiceDistribution". It contains all the third party extensions plus some "connector extensions" that integrate the functionality into BlueSpice (e.g. "BSDistConnector" makes sure that tags from "CategoryTree", "DynamicPageList", etc. show up int BlueSpice "InsertMagic" dialog).
I think in future this should be changed. The third party extensions sould be placed in extensions/ directory of MediaWiki rather than in extensions/BlueSpiceDistribution/.

An important thing: a BlueSpice/MediaWiki distribution only builds upon the latest release of an LTS version (e.g. REL1_23 or soon REL1_27).
Therefore all third party extensions that are distributed need to be of a version that is known to work with this particular MediaWiki release.

Last but not least we will need to implement/extend the installer (<mediawiki>/mw-config), so all extensions are being included in the proper order and some basic things (like settings) can be asked from the user.

My plan would be to have a fork of MediaWiki e.g. at https://github.com/hallowelt/bluespice. This repo would only hold the codebase of the lastest LTS release. Also the extensions/skins could be hold there. Either in form of git submodules/subtrees or just as the plain code files (copy&paste).
This way a user could just clone the repo and have a full distribution.

I'd really appreciate any feedback on this. From any side.

Event Timeline

Last but not least we will need to implement/extend the installer (<mediawiki>/mw-config), so all extensions are being included in the proper order

Order of including extensions really shouldn't matter (imo). If an extension depends on ordering that's a bug in the extension. (I think at least. Of course, I don't know the context here, so maybe there are legit exceptions to this rule that I just haven't thought about)

some basic things (like settings) can be asked from the user.

I agree, this is something we should support in the installer. Now that we have extension.json, maybe specifying install options in the json file would be easier than it would have been previously. Or maybe we could have some sort of callback that extensions could specify - but seems nicer to not execute arbitrary extension code during the installer, so I like the first approach better.

The third party extensions sould be placed in extensions/ directory of MediaWiki rather than in extensions/BlueSpiceDistribution.

Hmm, I could see either way on this.

If you want to generally distribute mediawiki in a bundle, then putting them in the extensions/ directory probably makes the most sense. But it might make equal sense to just distribute all the extensions as one master extension (BlueSpiceDistribution) which people could install in the usual way. Both approaches seem to have pro's and con's, and I'm not sure if either is necessarily better than the other.

@Nikerabbit, @Bawolff: Thanks for your replies.

"MediaWiki Language Extension Bundle" and "Semantic Bundle" are collections of extensions. Those are tarballs hosted somewhere public (e.g. google docs). I want to go a step further. I want to also redistribute the current LTS version of MediaWiki with it. So if someone wants to set up a "BlueSpice MediaWiki" he/she just needs to git clone it or download the latest tarball from a public git repo (e.g. github.com). One could also update the whole distribution by just doing a git pull.

About the "Order of including extensions": In general I am absolutely with you. But there are a few extensions that actually depend on each other (e.g. NSFileRepo bails out with a FATAL if Lockdown was not included before). As we can not fix all those "bugs" in the extensions that we want to distribute, we will need to deal with them.

About the "extension settings in installer": I like the idea of making the installer extenable by extensions. Using the extension.json is probably a good way but I would prefer the "callback" approach, as it allows a lot more flexibility (custom JavaScript/CSS, custom form field types, ...). Either way this is something that is not implemented yet and probably will not be until after LTS 1.27. Therefore we will need to stick with the "old way" and modify mw-config/overrides.php like described on docs/distributors.txt.

About "extensions/BlueSpiceDistribution": I want to get away from this because in some cases it makes hacks necessary. By changing the path of an extension most resources (JS, CSS, ...) need to be registered differently (e.g. using $IP/extensions/BlueSpiceDistribution as base path instead of just $IP/extensions). I believe an extension should be placed at the level that the author intended it to be placed.

Having an extension, or an extension group, use the installer is imho hardly possible without the extension or group supplying its own additions to the installer, including dependencies and i18n.

If there are interdependencies between extensions in a group, a group installer must be able to override parts of the extensions installer code. This is not a trivial thing to do in php.

@Purodha: Well, having a defined list of extensions within a distribution would at least allow us to implement a "distribution specific installer" that respects the settings to those extensions, wouldn't it?

About the "Order of including extensions": In general I am absolutely with you. But there are a few extensions that actually depend on each other (e.g. NSFileRepo bails out with a FATAL if Lockdown was not included before). As we can not fix all those "bugs" in the extensions that we want to distribute, we will need to deal with them.

I think its more proper for such extensions to check dependencies in a wgExtensionFunctions callback.

I think its more proper for such extensions to check dependencies in a wgExtensionFunctions callback.

That's a good point. It seems to me, though, this only resolves the fatal error. However, I struggle to understand how this helps to get rid of the need to include extensions in a certain order. In the wgExtensionFunctions callback, I can stop loading the depending extension by not instantiating the extension class. But how would I defer instantiation until the dependency is resolved?

As we just want to redistribute existing extensions I think it is beyond our possibilities to fix those things within the extensions. Of course we might contribute changes to some of them in order to improve their quality and stability. But in the first place we want to deliver a set of extensions that work with the stable LTS release of MediaWiki.
By bundling them and taking care of all the little things (like "inclusion order") we can offer the users of the distribution a stable, easy to maintain (update/configure) system that is tailored for business intranet useage (e.g prefdefined settings for permissions/licenses/upload filetypes).

In this scenario a distribution-specific installer is crucial. But as the installer can not be extended/overwritten by extension code we will need to define our code in <mediawiki>/mw-config/overrides.php, which - unfortunately - is part of the core repo of MediaWiki. So we either fork the MediaWiki core repo LTS branch and add our code (together with git subtrees/submodules in extensions/ and skins/), or we use some build tool like grunt that packages MediaWiki core LTS, extensions, skins and our installer code in mw-config.
At the moment I prefer the "fork" idea.

I think its more proper for such extensions to check dependencies in a wgExtensionFunctions callback.

That's a good point. It seems to me, though, this only resolves the fatal error. However, I struggle to understand how this helps to get rid of the need to include extensions in a certain order. In the wgExtensionFunctions callback, I can stop loading the depending extension by not instantiating the extension class. But how would I defer instantiation until the dependency is resolved?

Well if you just need to know if the other extension is installed, then you can do class_exists() (or defined() if testing a constant). If you have very complex dependencies (e.g. The one extension would depend on the [dynamic] state of another, which depends on the state of another), things might be more complicated - but I've never seen an extension that actually needed that - maybe yours does? If so, I'd be interested in seeing a concrete example.

I believe this task can be closed now. Nothing to do here anymore.