Page MenuHomePhabricator

Display low numbers of page watchers to admins in XTools and ArticleInfo gadget
Open, In Progress, MediumPublic

Assigned To
Authored By
Sdkb
Aug 6 2025, 11:14 PM
Referenced Files
F65723749: image.png
Aug 7 2025, 10:47 PM
F65721712: image.png
Aug 7 2025, 11:36 AM
F65721710: image.png
Aug 7 2025, 11:36 AM
F65721702: image.png
Aug 7 2025, 11:32 AM
F65721700: image.png
Aug 7 2025, 11:32 AM

Description

Administrators (at least on en-WP) have access to the number of watchers of a page when it is less than 30. However, it seems this info shows up neither at XTools (when logged in as an admin) nor in the ArticleInfo gadget that shows article statistics below the title. It would be nice for this info to display to users who have permission to see it in both places.

Originally requested here, where @MusikAnimal mentioned it was related to T220475.

Gadget examples

image.png (240×1 px, 74 KB)

image.png (237×1 px, 84 KB)

XTools examples

image.png (318×637 px, 28 KB)

image.png (303×608 px, 25 KB)

Event Timeline

Alien333 triaged this task as Medium priority.Aug 6 2025, 11:21 PM
Alien333 moved this task from Backlog to Page History on the XTools board.
Alien333 subscribed.

For in xtools.wmcloud.org: the idea would be to sign our api requests with the oauth login credentials in session. If I'm reading OAuth docs correctly (?), what we'd need for this would be

  1. get a new OauthClient and RequestStack in src/Repository/Repository.php
  2. get the session storage from the request stack
  3. get the accessToken from session storage (src/Controller/DefaultController helpfully stores it)
  4. change executeApiRequest from using guzzle with the params, to getting the complete query url, then using MediaWiki\OAuthClient\Client::makeOAuthCall ([1]) with that and the token.

Sample code:

/* at top */
use MediaWiki\OauthClient\Client;
use MediaWiki\OauthClient\ClientConfig;
use MediaWiki\OauthClient\Consumer;
use Symfony\Component\HttpFoundation\RequestStack;
/* in executeApiRequest */
$params = array_merge([
    'action' => 'query',
    'format' => 'json',
], $params);
$session = $this->requestStack->getSession(); // With requestStack being injected into __construct (which means injected in about the whole world, but eh). Perhaps we could make it nullable, but it's probably safer to make all queries with authentification
$accessToken = $session->get('oauth_access_token', null);
if ($accessToken) {
  $oauthEndpoint = $project->getUrl(false) . $project->getScript() . '?title=Special:OAuth'; // Duplicates some of Controller/DefaultController.php's getOauthClient. Perhaps the controller ought to pass in the client directly? but getOauthClient does say "(This shouldn't really be in this class, but oh well.)". And anyhow for now getOauthClient only does it for meta. *shrug*
  $conf = new ClientConfig($oauthEndpoint);
  $conf->setConsumer($this->consumer); // With consumer injected into __construct. Needed because requires access to a controller's getParameter
  $this->oauthClient = new Client($conf);
  $queryString = http_build_query($params);
  $requestUrl = $project->getApiUrl() . '?' . $queryString;
  return json_decode($this->oauthClient->makeOAuthCall($accessToken, $requestUrl));
} else { // Not logged in, default to a not-logged-in query
  return json_decode($this->guzzle->request('GET', $project->getApiUrl(), $params),
    ->getBody()->getContents(), true);
}
/* We should perhaps tweak the catch. In case of curl failure, makeOAuthCall sadly throws a generic Exception. IMO we should just throw a 502 whatever the exception type, i.e. */
} catch (Exception $e) {
  throw new BadGatewayException('api-error-wikimedia', ['Wikimedia'], $e);
}

For the gadget: IMO the simplest way would be adding a "login to XTools" or similar link, and then given it queries essentially the same code, we could just do the same as above (in both cases we'd have login credentials somewhere in session when doing request).

Oh, and:

Is there any precedent for having XTools display different data to admins vs non admins? If not, then might make sense to decline the xtools.wmcloud.org part of this task.

As for the gadget, I think it'd make sense to add this functionality if a MediaWiki Action API query exists to fetch this information. For simplicity, if such an Action API query exists, it doesn't need to go through XTools. I'll take a look and see if I can find an Action API for an uncensored # of page watchers for admins.

@Novem_Linguae : We already refuse to do certain queries for non-logged-in users. Also, the action API takes care automatically of showing or not showing depending on user rights. E.g., if you're logged in and go to https://en.wikisource.org/w/api.php?action=query&prop=info&inprop=watchers&titles=User:Alien333 (where you are not an admin), you won't get any; if you go to https://en.wikipedia.org/w/api.php?action=query&prop=info&inprop=watchers&titles=User:Alien333 (where you are an admin), you'll get it. With OAuth, we can just sign the requests which normally will let the api check whether the user the requests are made on the behalf of is an admin, and show/hide accordingly (note, just in case: we already use the action api for this query, including over at xtools.wmcloud.org).

Alien333 changed the task status from Open to In Progress.Aug 7 2025, 11:50 AM
Alien333 claimed this task.

Where's the official code for pageinfo-gadget.js live? Is it in GitHub somewhere? https://www.mediawiki.org/w/index.php?title=XTools/ArticleInfo.js says that it's at https://xtools.wmcloud.org/pageinfo-gadget.js, but that's probably not a good spot for it unless it's also in GitHub. That spot is not very accessible to non-maintaners, not very PR-able, etc.

Turned out to be simpler than I thought, in the end. We already had most of the stuff required stored in session. Done in PR #567.

First off, I'm sorry it's taken so long for me to chime in here. Alien333 has been doing amazing work and to state the obvious, I'm struggling to keep up.

The solution of making the requests through OAuth is clever. However, I worry about all those auth checks on each and every pageview made by a user of the gadget. We already have T337733: Fetching sessions from the Trove database sometimes times out, which presumably would mean the whole request to XTools would fail and they'd get no data at all. There's also T224382 which is about sessions not persisting long enough. And finally, there T382998: Add authentication to the XTools API – though I think don't think we subject /api/pageinfo (aka ArticleInfo gadget) to that, since it's a cheap query and we don't want to make users of the gadget supply an API key.

All in all, even though it would involve an additional query, I think querying the MediaWiki API directly from the gadget is the better solution.

Again, I should have said something earlier. I apologize for that :(

OAuth signing tested, works:

image.png (129×432 px, 12 KB)

(example here with /pageinfo/en.wikisource.org/User:Alien333, because I'm not an enwp admin so can't test there)

Only left to make the gadget make the request directly to api.php.

Perhaps there should be another task for it, but as a related matter, I also wonder about the choice to display nothing rather than displaying "less than 30 watchers" for non-admins. Editors might otherwise wonder why they see the count on some pages but not others. And knowing that a page has few watchers is still useful info, even if one doesn't know exactly how few.

Why not. There would be the concern, though, of hardcoding the value 30. $wgUnwatchedPageThreshold is currently set to 30 for Wikimedia installations, based on a long-gone toolserver tool, which itself took that from more or less discussion with the two then-toolserver admins, both of which did so I think in their capacity as WMDE employees, and neither of which I can find a presence of onwiki. From the opposite perspective, it's been stable for a long while now, it's not exposed anywhere (AFAICS), and "Not many" isn't very useful information, so... (The watcher count is just left falsey if there aren't enough; the api gives no more precisions.)