Creating sections with Scribunto with edit link

323 Views Asked by At

I'm trying to create a Scribunto module which, among other things, can create a section header in its output.

If the returned string contains, for example, == Hello World ==, the resulting page correctly displays the section, and even includes the section in the TOC. However, the section has no editsection link.

To some extent, this is understandable; the section doesn't actually exist in the page's source. But I would like to be able to put an edit link to where the content of the section is coming from. I have tried two different versions of a buildHeader function:

-- version 1:
function p.buildHeader(level, title, page)
    local open = '<span class="mw-editsection-bracket">[</span>'
    local close = '<span class="mw-editsection-bracket">]</span>'
    local link = '<a href="/w/index.php?title='..p.urlsafe(page)..'&action=edit" title="Edit section: '..title..'">edit</a>'
    local edit = '<span class="mw-editsection">'..open..link..close..'</span>'
    local text = '<span id="'..p.urlsafe(title)..'" class="mw-headline">'..title..'</span>'
    return '<h'..level..'>'..title..edit..'</h'..level..'>'
end

-- version 2:
function p.buildHeader(level, title, page)
    local result = mw.html.create('h'..level)
    result:tag('span')
            :attr({id=p.urlsafe(title), class='mw-headline'})
            :wikitext(title)
            :done()
        :tag('span')
            :attr('class', 'mw-editsection'):tag('span')
                :attr('class', 'mw-editsection-bracket')
                :wikitext('[')
                :done()
            :tag('a')
                :attr({href='/w/index.php?title='..p.urlsafe(page)..'&action=edit', title='Edit section: '..title})
                :wikitext('edit')
                :done()
            :tag('span')
                :attr('class', 'mw-editsection-bracket')
                :wikitext(']')
                :allDone()

    return tostring(result)
end

In both cases, the HTML of the anchor tag was escaped (eg, <span class="mw-editsection">...&lt;a href="..." title="..."&gt;edit&lt;/a&gt;</span>), and the whole mw-editsection span was included in the TOC text.

Is there any way for me to get my arbitrary edit link in there, or do I have to live with the editsection-less Scribunto sections?

2

There are 2 best solutions below

0
On BEST ANSWER

My working solution (but not my preferred solution) is to insert the link with JavaScript. The buildHeader function becomes:

function p.buildHeader(level, title, page)
    local result = mw.html.create('h'..level)
    result:attr('data-source', page):wikitext(title)
    return tostring(result)
end

Then, in MediaWiki:Common.js, I add:

$('h1[data-source],h2[data-source],h3[data-source],h4[data-source],h5[data-source],h6[data-source]').append(function() {
    var source = $(this).data('source'),
        title = $(this).text(),
        $editsection = $('<span>').attr('class', 'mw-editsection'),
        $open = $('<span>').attr('class', 'mw-editsection-bracket').text('['),
        $close = $('<span>').attr('class', 'mw-editsection-bracket').text(']'),
        $link = $('<a>').attr('title', 'Edit section: ' + title)
                        .attr('href', '/w/index.php?title=' + source + '&action=edit')
                        .text('edit');
    $editsection.append($open).append($link).append($close);
    return $editsection;
});
0
On

What you are trying to do is almost possible without JavaScript, but not quite.

Attempt #1: normal headings

First off, the reason a straightforward == Hello World == doesn't work from Scribunto is that wikitext that you generate in Scribunto isn't automatically preprocessed by the MediaWiki parser. From the Scribunto docs:

The module function should usually return a single string; whatever values are returned will be passed through tostring() and then concatenated with no separator. This string is incorporated into the wikitext as the result of the {{#invoke:}}.

At this point in the page parse, templates have already been expanded, parser functions and extension tags have already been processed, and pre-save transforms (e.g. signature tilde expansion and the pipe trick) have already happened. Therefore the module cannot use these features in its output text. For example, if a module returns "Hello, [[world]]! {{welcome}}", the page will read "Hello, world! {{welcome}}".

To get around this you can use Scribunto's frame:preprocess method to preprocess strings using the MediaWiki parser before you output them. For example, preprocessing == Hello World == will register the heading with the parser, and add a strip marker before the heading title. Then later on in the parsing process, the parser removes the strip marker and adds the edit section link.

You can do this with the following module code:

local p = {}

function p.main(frame)
    return frame:preprocess("== Hello World ==")
end

return p

When invoked on a wiki page, this code will give you a legitimate MediaWiki edit link. Unfortunately, it is a link to the first section of the Module page, and clicking on it will give you a "section editing not supported" error.

You can sort of work around this by using a different frame object to do the preprocessing. If you want the edit section link to point to the page where you invoked the module from, you can use the parent frame:

local p = {}

function p.main(frame)
    return frame:getParent():preprocess("== Hello World ==")
end

return p

And if you want the link to point to an arbitrary page, you can create a new child frame:

local p = {}

function p.main(frame)
    local childFrame = frame:newChild{title = "Your page here"}
    return childFrame:preprocess("== Hello World ==")
end

return p

The problem with the above two examples is that they both point to the first section on the page - and section headings added via #invoke don't count towards the section count. So unless you know that the first section is the one you need to edit, these links won't work for you. Unfortunately, I don't know of any way to change the section number of the edit links generated in this manner.

Attempt #2: fake headings

You were actually quite close in your attempt to emulate the heading HTML. The problem you were having is that MediaWiki doesn't allow <a> tags in wikitext to prevent cross-site scripting attacks. You can easily work around this by using MediWiki's external link syntax ([http://www.example.com Example]).

Here's something closer to what you were aiming for:

local p = {}

function p.makeHeading(title, page)
    local result = mw.html.create('h2')
    result
        :tag('span')
            :attr({id = title, class='mw-headline'})
            :wikitext(title)
            :done()
        :tag('span')
            :addClass('mw-editsection')
            :addClass('plainlinks')
            :tag('span')
                :attr('class', 'mw-editsection-bracket')
                :wikitext('[')
                :done()
            :wikitext(string.format(
                '[%s %s]',
                tostring(mw.uri.fullUrl(page, {action = 'edit'})),
                'edit'
            ))
            :tag('span')
                :attr('class', 'mw-editsection-bracket')
                :wikitext(']')
    return tostring(result)
end

return p

This will give you a heading that links to the right place and looks almost exactly like the original. There are just two problems:

  1. The link colour is slightly wrong. In MediaWiki, external links are given a slightly different colour to internal links. Here we are using an "external" link to the page's edit-mode URL, but edit links generated by MediaWiki are given the internal link colour. It is possible to work around this with judicious use of styles, but that leaves problem number two:
  2. The "[edit]" text is included in the table of contents.

Unfortunately, I'm not aware of a way to keep the "[edit]" text out of the table of contents without using JavaScript. So, the only solution that will work reliably and make everything look right is by using JavaScript as you have done.