ResourceLoader: Basic client side user preferences

This allows the body classes of skins to be customized for anonymous
users. Enable using $wgResourceLoaderClientPreferences = true;

* Only classes of the form <prefix>-(disabled|enabled)
  can be toggled.
* For now no client side API is provided as this should not be
  considered stable.
* Storage mechanism is cookie, stored under "mwclientprefs"
* Preferences apply to all skins. This means setting a preference
  in Vector 2022  would also lead to class manipulation in Minerva.
  This is by design to allow for skin-agnostic preferences. Up to
  caller to make sure the class being manipulated is limited to the
  skin if required ie. don't use generic classes.
* Avoids try/catch and JSON parsing by storaging as a string
* Places inline script before body tag before first stylesheet to
  avoid breaking the article's ability to parse the article
  concurrently with stylesheet download.

Usage:
Given a document with classes
"client-js vector-feature-limited-width-enabled ext-feature-enabled"

Set:
    document.cookie = 'mwclientprefs=vector-feature-limited-width'

Will result in toggling off the limited width.

Bug: T321498
Change-Id: Ic3b6eec19953932c697ab5bf48c33a4ac1841b07
This commit is contained in:
Jon Robson 2023-01-19 16:45:02 -08:00 committed by Krinkle
parent 66682881f1
commit d4d6156c3b
7 changed files with 66 additions and 1 deletions

View file

@ -3202,6 +3202,11 @@ config-schema:
via the `useskin` query parameter. To uninstall a skin, remove its inclusion
from LocalSettings.php.
@see \SkinFactory::getAllowedSkins
ResourceLoaderClientPreferences:
default: false
description: |-
Whether skins support client side (anonymous) preferences.
@see RL/ClientHtml
DisableOutputCompression:
default: false
description: 'Disable output compression (enabled by default if zlib is available)'

View file

@ -1990,6 +1990,12 @@ class MainConfigNames {
*/
public const SkipSkins = 'SkipSkins';
/**
* Name constant for the ResourceLoaderClientPreferences setting, for use with Config::get()
* @see MainConfigSchema::ResourceLoaderClientPreferences
*/
public const ResourceLoaderClientPreferences = 'ResourceLoaderClientPreferences';
/**
* Name constant for the DisableOutputCompression setting, for use with Config::get()
* @see MainConfigSchema::DisableOutputCompression

View file

@ -5152,6 +5152,15 @@ class MainConfigSchema {
'type' => 'map',
];
/**
* Whether skins support client side (anonymous) preferences.
*
* @see RL/ClientHtml
*/
public const ResourceLoaderClientPreferences = [
'default' => false,
];
/**
* Disable output compression (enabled by default if zlib is available)
*/

View file

@ -21,6 +21,7 @@
namespace MediaWiki\ResourceLoader;
use Html;
use MediaWiki\MainConfigNames;
use Wikimedia\WrappedString;
use Wikimedia\WrappedStringList;
@ -289,6 +290,15 @@ RLPAGEMODULES = {$pageModulesJson};
";
}
$config = $this->resourceLoader->getConfig();
$user = $this->context->getUserIdentity();
$isAnon = !$user || !$user->isRegistered();
// This code is only loaded for anonymous users. Logged in users should use preferences.
if ( $config->get( MainConfigNames::ResourceLoaderClientPreferences ) && $isAnon ) {
$script .= $this->getClientSidePreferencesScript(
$config->get( MainConfigNames::CookiePrefix )
);
}
if ( !$this->context->getDebug() ) {
$script = ResourceLoader::filter( 'minify-js', $script, [ 'cache' => false ] );
}
@ -383,6 +393,32 @@ RLPAGEMODULES = {$pageModulesJson};
return $ret;
}
/**
* Adds ability for anonymous users to change classes on document.documentElement
*
* @param string $cookiePrefix
* @return string
*/
private function getClientSidePreferencesScript( string $cookiePrefix ) {
return <<<END
(function () {
// Client side preferences
var doc = document.documentElement;
var clientPrefCookie = document.cookie.match(/(?:^|; )${cookiePrefix}mwclientprefs=([^;]+)/);
// For now, only support disabling a feature
// Only supports a single feature (modifying a single class) at this stage.
// In future this may be expanded to multiple once this has been proven as viable.
if ( clientPrefCookie ) {
var featureName = clientPrefCookie[1];
doc.className = doc.className.replace(
featureName + '-enabled',
featureName + '-disabled'
);
}
} () );
END;
}
/**
* Explicitly load or embed modules on a page.
*

View file

@ -645,6 +645,7 @@ return [
'FallbackSkin' => 'fallback',
'SkipSkins' => [
],
'ResourceLoaderClientPreferences' => false,
'DisableOutputCompression' => false,
'FragmentMode' => [
0 => 'html5',

View file

@ -1974,6 +1974,12 @@ $wgFallbackSkin = null;
*/
$wgSkipSkins = null;
/**
* Config variable stub for the ResourceLoaderClientPreferences setting, for use by phpdoc and IDEs.
* @see MediaWiki\MainConfigSchema::ResourceLoaderClientPreferences
*/
$wgResourceLoaderClientPreferences = null;
/**
* Config variable stub for the DisableOutputCompression setting, for use by phpdoc and IDEs.
* @see MediaWiki\MainConfigSchema::DisableOutputCompression

View file

@ -348,7 +348,9 @@ class ClientHtmlTest extends \PHPUnit\Framework\TestCase {
}
private static function makeContext( $extraQuery = [] ) {
$conf = new HashConfig( [] );
$conf = new HashConfig( [
'ResourceLoaderClientPreferences' => false
] );
return new Context(
new ResourceLoader( $conf, null, null, [
'loadScript' => '/w/load.php',