DEV Community

Luca Mauri
Luca Mauri

Posted on • Edited on

Messages on Mediawiki sidebar with Javascript

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, 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
Enter fullscreen mode Exit fullscreen mode

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, 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 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, like:

var conf = mw.config.get([
    'wgServer',
    'wgWikibaseItemId',
    'wgPageName'
]);
Enter fullscreen mode Exit fullscreen mode

then generate the URL and call the function exactly like the example:

ModifySidebar('add', 'toolbox', messageName, conf.wgServer + '/wiki/Special:EntityData/' + ItemName[1] + '.json');
Enter fullscreen mode Exit fullscreen mode

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…
Enter fullscreen mode Exit fullscreen mode

I originally looked at the documentation on message handling, 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.
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

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);
        });
    });
}
Enter fullscreen mode Exit fullscreen mode

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 Tools group
  • pertains Wiki Base
  • 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

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');
    }
}
Enter fullscreen mode Exit fullscreen mode

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

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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

aNode.text = mw.message(idName).text();
Enter fullscreen mode Exit fullscreen mode

and in

aNode.setAttribute( 'title', mw.message('tooltip-' + idName).text());
Enter fullscreen mode Exit fullscreen mode

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
…
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)