Page MenuHomePhabricator

Skin hooks should run after extension hooks
Open, Needs TriagePublicBUG REPORT

Description

Skins make use of hooks to allow extensions to integrate themselves into the skin and support the extension-ecosystem we have. However, often skins need to make last minute modifications based on the data collected via extensions - for example in some cases it may want to modify HTML to support icons, it may want to disable certain features under certain circumstances, or prefer certain options over others when available.

What happens?:

Right now all hooks are registered and run in ain first in-first out order of registration. This meant that when rolling out a new version of Vector we hit a bug T287533. We fixed this by creating a skin function that essentially wrapped the hook so the skin could register it's hook using inheritance instead of hooks.

What should have happened instead?:

Ideally all skin hooks would be run after extension hooks. Since skins are responsible for the output this makes sense from a product point of view.

  • Notes **

In CI we enforce that extensions are loaded before skins so this issue was not picked up in our CI or beta cluster. However, it did surface as an issue in production.

Related: T212482

Related slack thread
https://wikimedia.slack.com/archives/C01R06P8D1B/p1626381636166400
(Sorry this is a private, but basically says all of the above in a less coherent way :))

Event Timeline

Jdlrobson renamed this task from Allow skins to run hooks last to Skin hooks should run after extension hooks.Jul 28 2021, 6:27 PM

That does not seem like an elegant solution. I don't understand why skins even need hooks. Skins are subclasses. A skin especially shouldn't have to hook its own parent, which is apparently the subject of T287533. Just have SkinTemplate call an empty protected method that SkinVector overrides.

I agree. The method in question already exists. Skin have the protected buildContentNavigationUrls method for this purpose.

buildContentNavigationUrls method for this purpos

Not really. The skin hooks wanted to run /after/ the extension hooks, but not after buildContentNavigationUrls. To solve this issue we refactored the code and pulled out a new method runOnSkinTemplateNavigationHooks.

Skins can run hooks directly after PersonalUrls hook by extending buildPersonalUrls method, as the hook is run at the end of the function but if buildPersonalUrls method body changes in future, that might not be a solution.

If extending skins methods is the way to go in future, wouldn't it make sense to deprecate the registration of skin hooks from inside skins to avoid this confusion?

Change 720080 had a related patch set uploaded (by Krinkle; author: Krinkle):

[mediawiki/skins/Vector@master] Use buildContentNavigationUrls() override instead of odd hook calling

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

Change 720081 had a related patch set uploaded (by Krinkle; author: Krinkle):

[mediawiki/core@master] skins: Revert \"SkinTemplate: Factor out SkinTemplateNavigation hooks\"

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

I ran into the same issue recently with a skin that I am working on.

My use case is to add icons to each item in the TOOLBOX menu.
Extensions (e.g. CiteThisPage, Wikidata) often append items to the TOOLBOX menu through the SidebarBeforeOutput hook.
Since SidebarBeforeOutput is not guaranteed to run last in a skin, the skin has no visibility of the items added by extensions, which makes it impossible to append icons to those items added by extensions.

From my brief read it seems that Vector 2022 have the same issue with the SkinTemplateNavigation hook, in which they have to bypass it with a hack.

@alistair3149 I think that particular problem will be addressed whenever we get round to doing T315016 and T315015 (at that point you'll be able to extend the functionality in Skin::runOnSkinTemplateNavigationHooks to add your icons). It's a long way away though :-(

In mean time, something like this might help (using 1.40 MediaWiki) ?? :

 public function getTemplateData() {
                $data = parent::getTemplateData();
                $items = $data['data-portlets-sidebar']['data-portlets-first']['array-items'];
                foreach( $items as $index => $item ) {
                               $item['html-item'] = Html::rawElement('li', [ 'class' => 'icon-'. $item['id'] ], $item['html'] );
                }
                return $data;
}

Currently my workaround was just asking people to load the skin last since it will ensure the hook to run at the end :-/
I tried to avoid tempering with HTML in PHP as much as possible. Hopefully T315015 will address the problem down the line, and it also makes much more sense that way for other uses.

It is not strictly related to this task, but my biggest concern is that the legacy hooks in T315015 will be hard deprecated before the next LTS. It might cause issue with skins that have to maintain a LTS support policy, like Citizen.

Change 720080 abandoned by Krinkle:

[mediawiki/skins/Vector@master] Use buildContentNavigationUrls() override instead of odd hook calling

Reason:

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

Change 720081 abandoned by Krinkle:

[mediawiki/core@master] skins: Revert "SkinTemplate: Factor out SkinTemplateNavigation hooks"

Reason:

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

Recent I ran into the same issue with onSkinEditSectionLinks and VisualEditor. My use case is to add icons to the edit section links by adding HTML classes to the edit section link with the onSkinEditSectionLinks hook.

The skin has no context of the visual edit link from VisualEditor because VisualEditor runs the onSkinEditSectionLinks hook after the skin does. And currently there isn't a good way to modify the behavior of edit section unfortunately.