Messages on Mediawiki sidebar with Javascript
Tags: mediawiki, javascript, wikibase, json
Published: 2021-03-26 16:59:21

This is the story of a seemingly simple and straightforward modification to a user interface that led me far away from where I started and that taught me lessons I'd like to share with the community here.

During the development of LUA code for the Wiki I manage, I often needs to see raw data from my [custom Wikibase instance](https://data.wikitrek.org), thus presented in JSON format.
Wikibase allows a very simple way of accessing JSON representation of any given item by accessing the *EntityData* special page with the syntax 
```
/Special:EntityData/Q1.json
```
Every time the user needs to access this page is necessary to go to the list of special pages, or remember the syntax and manually write the URL taking note of the item's number.
This is clearly inconvenient, so I looked for a better way to easily get the JSON I needed.
The *sidebar* of a Wikibase instance, under *Tools* group, contains a link named `Concept URI`, handy to quickly get the URI of the item in the main page. From here I took the idea of creating a link on the sidebar to easily reach the JSON content I needed.

## Easy start
I was already familiar with the sidebar customization as [explained in Mediawiki website](https://www.mediawiki.org/wiki/Manual:Interface/Sidebar), but in the past I used to make *static* modifications, like adding external links. In this case, on the contrary, I needed a way to dynamically generate a link and only for specific pages.

By attentively reading [further that same document](https://www.mediawiki.org/wiki/Manual:Interface/Sidebar#Add_or_remove_toolbox_sections_(JavaScript)) it became clear to me I could add a code fragment in `MediaWiki:Common.js` file to programmatically generate the link. The code example basically does already what I wanted to, I simply had to add a code to get the [specific page information](https://www.mediawiki.org/wiki/Manual:Interface/JavaScript#mw.config), like:
```javascript
var conf = mw.config.get([
    'wgServer',
    'wgWikibaseItemId',
    'wgPageName'
]);
```
then generate the URL and call the function exactly like the example:
```javascript
ModifySidebar('add', 'toolbox', messageName, conf.wgServer + '/wiki/Special:EntityData/' + ItemName[1] + '.json');
```
and that was it.
It worked fine, it seemed I solved the problem in a few minutes.

## Just one question
But then I asked myself: what about internationalization? The code example simply created a tag with the fixed text specified in `name` parameter, but my Wikibase instance is normally accessed both in Italian and English, so it would ideally need multi-language support.

Again, seemingly easy thing to do: MediaWiki provides a standard mechanism to use *messages*: the code contains placeholders string only, while the actual content of the string is defined in the user interface.
So I wanted to create a message called something like `t-menu-label` and being able to customize it by writing text in the pages:
```
MediaWiki:T-menu-label\it
MediaWiki:T-menu-label\en
MediaWiki:T-menu-label\de
and so on…
```
I originally looked at the [documentation on message handling]( https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Message), but, with that instructions, I was only able to handle messages *within the code*.
I couldn't find a way to let the user customize messages with the `MediaWiki:` mechanism explained above.

## Look closer
It unfortunately took me a lot of effort to understand that I should have used a different approach to the problem. In fact the method I needed to use was the one explained in [the message documentation](https://www.mediawiki.org/wiki/Manual:Messages_API#Using_an_API_query_from_JavaScript).
When I first read it, I was under the impression that this only was necessary to load **default** messages from MediaWiki core, so I discarded it as a way to use my own **custom** message.
I was wrong: this system can be used to load **any** message from MediaWiki: if the one requested does not exist in MediaWiki core, then it can be created as a custom one as explained above.

## The final solution
With this new understanding, I was finally able to put together the needed code to achieve my desired result.
Let's jump at it, with some comments.

### Call it
```javascript
jQuery(sideJSONMessage('t-wb-json'));

function sideJSONMessage(messageName) {
    jQuery.when(mw.loader.using(['mediawiki.api', 'mediawiki.jqueryMsg']), jQuery.ready ).done( function() {
        new mediaWiki.Api().loadMessagesIfMissing([messageName, 'tooltip-' + messageName]).done( function() {
            AddJSONLinkToSidebar(messageName);
        });
    });
}
```
This fragment of code starts the whole chain of events by identifying the name of the event to load, check that all the APIs are loaded and then proceeding to read the said message.

`t-wb-json` is both the name of the message and the element ID of the HTML tag we'll create, this follows the conventions already in use in the sidebar.
So, the menu `Concept URI` is identified by `t-wb-concept-uri`, my custom menu: 
* belongs to **T**ools group
* pertains **W**iki **B**ase
* is about a **JSON** data output

Thus `t-wb-json`, in short.

Beside `messageName`, another message called `'tooltip-' + messageName` will be loaded. This will be useful to further describe the menu item, as explain later below.

### Check page type
```javascript
function AddJSONLinkToSidebar(messageName) {
    var conf = mw.config.get([
	'wgServer',
	'wgWikibaseItemId',
	'wgCanonicalNamespace',
	'wgPageName'
    ]);
	
    var PageName = conf.wgPageName;
    var nameSpace = conf.wgCanonicalNamespace;
    var nameSpaceList = ['Item', 'Property', 'Lexeme']

    if (nameSpaceList.indexOf(nameSpace) > -1) {
        var ItemName = PageName.split(':');
        AddItemToSidebar('toolbox', messageName, conf.wgServer + '/wiki/Special:EntityData/' + ItemName[1] + '.json');
    }
}
```
With `mw.config.get` we can obtain all the useful information about the Wikibase instance and the current context.
All these data are needed to create the link, but *first* of all is necessary to check that the page actually represent an **item**, otherwise the JSON output would be meaningless and then non-existent.

---

**Update 2021-04-01**
I wrote a better code to check for the page type. The previous iteration used to analyze the substring for the text `item`, now I make use of `wgCanonicalNamespace` to programmatically check the page namespace in more consistent way.

---

### Do the job
```javascript
function AddItemToSidebar(section, idName, link) {
    var target;

    try {
        switch ( section ) {
            case 'languages':
                target = 'p-lang';
		break;
	    case 'toolbox':
                target = 'p-tb';
                break;
            case 'navigation':
                target = 'p-navigation';
		break;
            default:
                target = 'p-' + section;
                break;
        }

        var node = document.getElementById( target )
	    .getElementsByTagName( 'div' )[0]
            .getElementsByTagName( 'ul' )[0];

        var aNode = document.createElement( 'a' );
        var liNode = document.createElement( 'li' );

        aNode.text = mw.message(idName).text();
        aNode.setAttribute( 'href', link );
        aNode.setAttribute( 'title', mw.message('tooltip-' + idName).text());

        liNode.appendChild( aNode );
        liNode.id = idName;

        node.appendChild( liNode );

    } catch( e ) {
        // let's just ignore what's happened
        return;
    }
}
```

First of all, the function identifies the proper UL object by using the name of the section and then referencing the first DIV. Then a new LI is created to contain the A link.

All the magic happens in
```javascript
aNode.text = mw.message(idName).text();
```
and in
```javascript
aNode.setAttribute( 'title', mw.message('tooltip-' + idName).text());
```
where the *text* of the link is set by reading the message loaded in the first fragment of code, above.
In the same way, another message with the same label prefixed by `tooltip-` (again, loaded above in the first step) is assigned to `title` attribute: this will thus be shown as a tool-tip on mouse over.

Finally, the node is given the same name as the message as `id` attribute, for naming consistency, and all the element are properly concatenated into the DOM.

## Setting the text
Now that the code is ready, it is necessary to set the proper text for the messages. Remember that the code will ask Mediawiki engine for two messages called `t-wb-json` and `tooltip-t-wb-json`: if they are not set, a placeholder will be shown.

To fill them, let's simply create two pages named `MediaWiki:T-wb-json` and `MediaWiki:Tooltip-t-wb-json`. Their content will become the label and the tool-tip of the link, respectively.
This will work in the wiki's primary languages, but additional languages can simply be set by using subpages with the ISO code of the language, so we can create the pages
```
MediaWiki:T-wb-json/it
MediaWiki:T-wb-json/de
MediaWiki:T-wb-json/fr
…
MediaWiki:Tooltip-t-wb-json/it
MediaWiki:Tooltip-t-wb-json/de
MediaWiki:Tooltip-t-wb-json/fr
…
```
and fill them with the text in the proper language.

## Experiment more
This experience taught some interesting lessons about the inner workings of MediaWiki messages interface: a seemingly easy code actually hides a lot of complexity in research.

Another lesson I learnt is to experiment more and don't stop where I *think* the point is, because sometimes the point is somewhere, as I saw here.

Finally, I must unfortunately point out again that MediaWiki documentation is fragmented and not always very clear to the final user.