Page MenuHomePhabricator

Provide a parser function to create links to the wiki which don't look external
Open, MediumPublicFeature

Description

Wikitext is very limiting for links.

When people want to create local urls with parameters they abuse wikitext by doing the following:
[http://thislookslikeanexternallink.com/butitisactuallylocal?action=foo link]

This has the side effect of rendering external links which then have to be hacked around. Sigh.

As suggested by Bartosz we should create a {{#link:}} parser function so that in message keys we can do {{#link:/my/localurl/Foo?action=edit}}

Talking to Brion Vibber he says this is a good idea.


Version: 1.24rc
Severity: enhancement
See Also:
https://bugzilla.wikimedia.org/show_bug.cgi?id=54835

Details

Reference
bz66746

Related Objects

View Standalone Graph
This task is connected to more than 200 other tasks. Only direct parents and subtasks are shown here. Use View Standalone Graph to show more of the graph.

Event Timeline

bzimport raised the priority of this task from to Medium.Nov 22 2014, 3:19 AM
bzimport set Reference to bz66746.
bzimport added a subscriber: Unknown Object (MLST).

Note that this would be intended for localisation messages, which are currently full of <span class="plainlinks"> and {{fullurl:}} and "Click here to $1", and wouldn't be available elsewhere. We could possibly even limit it to specific messages with some Message class method if someone feels that it would be abused.

If you want a "parser function for creating arbitrary links", we already have three: https://www.mediawiki.org/wiki/Help:Magic_words#URL_data

Changing summary to reflect what seems to be the use case according to comment 0. It seems you just want something like $wgNoFollowDomainExceptions but for the "external" class, set by default for the wiki's domain?

For targets other than the current wikis, the most obvious way to make the links look internal (as they should if they're linked from the interface itself) is to use interwikis, which however were veto'ed by Brion, see bug 54835.

{{localurl:Special:Userlogin|returnto=Hello world}}
ouputs /w/index.php?title=Special:Userlogin&returnto=Hello world

but I want to output
<a href="/w/index.php?title=Special:Userlogin&returnto=Hello world">Log in</a>
as part of an i18n message.

currently the way to do this is
[$1 log in] where $1 is an absolute URL e.g.
http://localhost/w/index.php?title=Special:Userlogin&returnto=Hello world

This is problematic in mobile for 2 reasons

  1. Incorrectly styled and in markup as an external link
  2. Clicking this link will take you to the desktop site which will redirect you to the mobile site, so it increases the time it takes to click a link on mobile.

Please update the summary in such a way that it better reflects this, the current summary does not look right to me.

Change 167117 had a related patch set uploaded by EBernhardson:
Add {{#link:}} pf for creating arbitrary local links in i18n messages only

https://gerrit.wikimedia.org/r/167117

I think I might recommend making the parameters compatible with localurl -- this would also allow for porting code between wikis that have different installation directories.

For example one wiki might have /w/index.php and /wiki/Foo and another might have /index.php and /index.php/Foo ...

So you'd do {{#link:Special:Userlogin|returnto=Hello world}} rather than {{#link:/w/index.php?Special:Userlogin&returnto=Hello%20world}} and have the link automatically generate for the correct site config.

(In reply to Brion Vibber from comment #5)

I think I might recommend making the parameters compatible with localurl --
this would also allow for porting code between wikis that have different
installation directories.

Definitely a requirement. Plus points if this feature becomes a parameter of fullurl (or two), including the being bound to current domain rather than $wgCanonicalServer or whatever.

as this is intended for messages, my intended use case was more like:

{{#link:$1|$2}}

and

$msg->params( $title->getLinkUrl( 'action=edit' ), 'edit' );

As suggested this becomes

{{#link:$1|returnto=$2|$3}}

Which just moves some of the logic from the php into the message template. I don't think its desirable for the programmer or the translator to move this logic into the message. Alternatively the programmer will use the first example but stick 'Main_page|action=edit' into $1 which is equally undesirable. It is quite likely the caller has a Title object and know how to use it.

I don't remember where this came up last time, but back then I argued with Bartosz that this is just a special case of more general problem: placing html into message so that it wraps some translated content.

Just think about wanting to do a button instead of a link. Or what about adding classes or other attributes to the link? Are we going to add a new magic word for each special case? This solution might solve the issue for many cases, but I am not convinced it is flexible enough in the long run.

I even had proposed a syntax for this and some ideas how to implement it, but i cannot find them now.

I'm adding the missing i18n keyword which is the reason I didn't notice this bug earlier.

(In reply to Niklas Laxström from comment #8)

I even had proposed a syntax for this and some ideas how to implement it,
but i cannot find them now.

Did you find this?

No, it might have been in IRC somewhere. The idea is that there is parser function, which takes the html[1] and the translated string(s) as parameters.

Message:
"login": "Please {{#foo:$1|login now}}"

Usage:
$button = Html::element( 'button', array( 'class' => 'foo' ), '$1' );
wfMessage( 'login', $button )->escaped();

The escaping of "login now" should be strong enough that it can be used 1) inside elements and 2) inside attributes, e.g. title="$1". Use of placeholders in other positions should be discouraged or even prevented.

[1] Message class would need to work with Parser class to make this safe. Possibly, instead of giving the html directly to the parser function, Message class generates an unique token. The token is given to the parser function and the html associated with the token is given out of band to parser. The parser function will then fetch this html and substitute escaped parameters to it.

Hmm, that is a broader change. It may not be necessary if we settle on a templating/widget approach (https://www.mediawiki.org/wiki/Requests_for_comment/HTML_templating_library) in a reasonable amount of time.

Templating has started getting merged into core, but we haven't settled on a markup language yet so currently it's limited to HTML.

You will need a solution even with templating. The general problem of avoiding lego-messages by taking something in the translation string and putting it inside an html element, which is then put back into the translation string wont go away with templating.

As articulated above, #link might solve 90% of the cases, but I think with some extra effort we can reach a solution which solves 99%*.

* Numbers not based on data.

I don't remember where this came up last time, but back then I argued with Bartosz that this is just a special case of more general problem: placing html into message so that it wraps some translated content.

Just think about wanting to do a button instead of a link. Or what about adding classes or other attributes to the link? Are we going to add a new magic word for each special case? This solution might solve the issue for many cases, but I am not convinced it is flexible enough in the long run.

I've just realized that we can already do this, albeit in a slightly clunky way:

"login": "Please $1login now$2"

$buttonOpen = Html::openElement( 'button', array( 'class' => 'foo' ) );
$buttonClose = Html::closeElement( 'button' );
wfMessage( 'login' )->rawParams( $buttonOpen, $buttonClose )->parse();

The same technique could probably be used for links. (It isn't compatible with client-side messaging code, and would be very difficult to implement support for it there, though – that still needs lego messages for this case.)

So, personally I think that a dedicated special-case #link parser function would be useful (and happens to be easy to implement client-side too), and that a dedicated parser function for wrapping arbitrary text in arbitrary wrappers would be less useful.

Since such a thing couldn't output wikitext, it would be better as an extension tag than as a parser function.

Since such a thing couldn't output wikitext, it would be better as an extension tag than as a parser function.

I think that would also solve the weird nonsensical interaction when used with wfMessage()->text(). Parser functions are not parsed with ->text(). But then again, it would not work with ->escaped() anymore either.

Another way we might look at this bug is think about updating the parser so that [http://currenthost/Special:Userlogin?type=signup signup] actually gets turned into a local url (e.g. parser notices the url you are using is local and resolves it to the be a relative url rather than making it look like an absolute url / external link).

There was some chat on the topic at http://bots.wmflabs.org/~wm-bot/logs/%23wikimedia-dev/20141223.txt after about 17:27.

Another way we might look at this bug is think about updating the parser so that [http://currenthost/Special:Userlogin?type=signup signup] actually gets turned into a local url

That's T13477.

Change 167117 abandoned by EBernhardson:
Add <a> extension tag for creating arbitrary local links in i18n messages only

https://gerrit.wikimedia.org/r/167117

Nemo_bis set Security to None.

Change 167117 abandoned by EBernhardson:

Can this be closed as declined/invalid/duplicate of T13477?

No, that's a different issue, although there is some overlap.

Aklapper changed the subtype of this task from "Task" to "Feature Request".Feb 4 2022, 12:23 PM

One way this could be implemented is not via a separate parser function, but via allowing [{{localurl:Page}} text] syntax to actually produce working links. Then such links that are 100% to the current wiki (if they start with /w/index.php or whatever $wgServer is configured to) can be marked to not have any external classes.

It would be actually more practical to do it this way since the parser function would require template authors across multiple wikis to re-write their code entirely, as well as to have Lua support. There is already Lua support for both fullurl: and localurl:, and templates are already configured for fullurl:. Though some issues between two implementations like T26343: localurl discards section id would need to be fixed.

(Additionally, it would cut down on some template limits and HTML output size on bigger pages using templates.)