Revert "Revert "Revert "End migration mode"""
This reverts commit 8f5a057027
.
Reason for revert: I was wrong, this was not deployed and it's causing
lots of other errors. Reverting for real this time, and leaving it
alone.
Change-Id: Idb8f3ca8fa4cbc2fe4a3359d45a7fd0127dace9d
This commit is contained in:
parent
8f5a057027
commit
becf14ef61
24
README.md
24
README.md
|
@ -29,6 +29,30 @@ project are noted at:
|
|||
|
||||
<https://www.mediawiki.org/wiki/Reading/Web/Coding_conventions>
|
||||
|
||||
URL query parameters
|
||||
--------------------
|
||||
|
||||
- `useskinversion`: Like `useskin` but for overriding the Vector skin version
|
||||
user preference and configuration. E.g.,
|
||||
http://localhost:8181?useskin=vector&useskinversion=2.
|
||||
|
||||
Skin preferences
|
||||
----------------
|
||||
|
||||
Vector defines skin-specific user preferences. These are exposed on
|
||||
Special:Preferences when the `VectorShowSkinPreferences` configuration is
|
||||
enabled. The user's preference state for skin preferences is used for skin
|
||||
previews and any other operation unless specified otherwise.
|
||||
|
||||
### Version
|
||||
|
||||
Vector defines a "version" preference to enable users who prefer the December
|
||||
2019 version of Vector to continue to do so without any visible changes. This
|
||||
version is called "Legacy Vector." The related preference defaults are
|
||||
configurable via the configurations prefixed with `VectorDefaultSkinVersion`.
|
||||
Version preference and configuration may be overridden by the `useskinversion`
|
||||
URL query parameter.
|
||||
|
||||
### Pre-commit tests
|
||||
|
||||
A pre-commit hook is installed when executing `npm install`. By default, it runs
|
||||
|
|
|
@ -43,6 +43,20 @@ final class Constants {
|
|||
*/
|
||||
public const SERVICE_FEATURE_MANAGER = 'Vector.FeatureManager';
|
||||
|
||||
// These are tightly coupled to skin.json's config.
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const CONFIG_KEY_SHOW_SKIN_PREFERENCES = 'VectorShowSkinPreferences';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const CONFIG_KEY_DEFAULT_SKIN_VERSION = 'VectorDefaultSkinVersion';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const CONFIG_KEY_DEFAULT_SKIN_VERSION_FOR_EXISTING_ACCOUNTS =
|
||||
'VectorDefaultSkinVersionForExistingAccounts';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
|
|
@ -22,14 +22,31 @@
|
|||
|
||||
namespace Vector\FeatureManagement\Requirements;
|
||||
|
||||
use MediaWiki\User\UserOptionsLookup;
|
||||
use User;
|
||||
use Vector\Constants;
|
||||
use Vector\FeatureManagement\Requirement;
|
||||
use WebRequest;
|
||||
use Vector\SkinVersionLookup;
|
||||
|
||||
/**
|
||||
* Checks if the current skin is modern Vector.
|
||||
* Retrieve the skin version for the request and compare it with `Constants::SKIN_VERSION_LATEST`.
|
||||
* This requirement is met if the two are equal.
|
||||
*
|
||||
* Skin version is evaluated in the following order:
|
||||
*
|
||||
* - `useskinversion` URL query parameter override. See `README.md`.
|
||||
*
|
||||
* - User preference. The `User` object for new and existing accounts are updated by hook according
|
||||
* to the `VectorDefaultSkinVersionForNewAccounts` and
|
||||
* `VectorDefaultSkinVersionForExistingAccounts` config values. See the `Vector\Hooks` class and
|
||||
* `skin.json`.
|
||||
*
|
||||
* If the skin version is evaluated prior to `User` preference hook invocations, an incorrect
|
||||
* version may be returned as only query parameter and site configuration will be known.
|
||||
*
|
||||
* - Site configuration default. The default is controlled by the `VectorDefaultSkinVersion` config
|
||||
* value. This is used for anonymous users and as a fallback configuration. See `skin.json`.
|
||||
*
|
||||
* This majority of this class is taken from Stephen Niedzielski's `Vector\SkinVersionLookup` class,
|
||||
* which was introduced in `d1072d0fdfb1`.
|
||||
*
|
||||
* @unstable
|
||||
*
|
||||
|
@ -39,31 +56,17 @@ use WebRequest;
|
|||
final class LatestSkinVersionRequirement implements Requirement {
|
||||
|
||||
/**
|
||||
* @var WebRequest
|
||||
* @var SkinVersionLookup
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @var UserOptionsLookup
|
||||
*/
|
||||
private $userOptionsLookup;
|
||||
private $skinVersionLookup;
|
||||
|
||||
/**
|
||||
* This constructor accepts all dependencies needed to obtain the skin version.
|
||||
*
|
||||
* @param WebRequest $request
|
||||
* @param User $user
|
||||
* @param UserOptionsLookup $userOptionsLookup
|
||||
* @param SkinVersionLookup $skinVersionLookup
|
||||
*/
|
||||
public function __construct( WebRequest $request, User $user, UserOptionsLookup $userOptionsLookup ) {
|
||||
$this->request = $request;
|
||||
$this->user = $user;
|
||||
$this->userOptionsLookup = $userOptionsLookup;
|
||||
public function __construct( SkinVersionLookup $skinVersionLookup ) {
|
||||
$this->skinVersionLookup = $skinVersionLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,14 +81,6 @@ final class LatestSkinVersionRequirement implements Requirement {
|
|||
* @throws \ConfigException
|
||||
*/
|
||||
public function isMet(): bool {
|
||||
$useSkin = $this->request->getVal( 'useskin' );
|
||||
$user = $this->user;
|
||||
if ( !$useSkin && $user->isSafeToLoad() ) {
|
||||
$useSkin = $this->userOptionsLookup->getOption(
|
||||
$user,
|
||||
Constants::PREF_KEY_SKIN
|
||||
);
|
||||
}
|
||||
return $useSkin === Constants::SKIN_NAME_MODERN;
|
||||
return $this->skinVersionLookup->getVersion() === Constants::SKIN_VERSION_LATEST;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace Vector\HTMLForm\Fields;
|
||||
|
||||
use Vector\Constants;
|
||||
|
||||
/**
|
||||
* The field on Special:Preferences (and Special:GlobalPreferences) that allows the user to
|
||||
* enable/disable the legacy version of the Vector skin. Per
|
||||
* https://phabricator.wikimedia.org/T242381, the field is a checkbox, that, when checked, enables
|
||||
* the legacy version of the Vector skin.
|
||||
*
|
||||
* `HTMLLegacySkinVersionField` adapts the boolean storage type of a checkbox field to the string
|
||||
* storage type of the Vector skin version preference (e.g. see `Constants::SKIN_VERSION_LEGACY`).
|
||||
*
|
||||
* However, we cannot extend `HTMLCheckField` to inherit the behavior of a checkbox field.
|
||||
* `HTMLCheckField::loadDataFromRequest` returns boolean values. Returning non-boolean values in
|
||||
* `HTMLLegacySkinVersionField::loadDataFromRequest` would violate Liskov's Substitution Principle.
|
||||
* Like `HTMLExpiryField`, `HTMLLegacySkinVersionField` proxies to a private instance of
|
||||
* `HTMLCheckField`, adapting parameter and return values where necessary.
|
||||
*
|
||||
* @package Vector\HTMLForm\Fields
|
||||
* @internal
|
||||
*/
|
||||
final class HTMLLegacySkinVersionField extends \HTMLFormField {
|
||||
|
||||
/**
|
||||
* @var \HTMLCheckField
|
||||
*/
|
||||
private $checkField;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function __construct( $params ) {
|
||||
/**
|
||||
* HTMLCheckField must be given a boolean as the 'default' value.
|
||||
* Since MW 1.38.0-wmf.9, we could be given a boolean or a string.
|
||||
* @see T296068
|
||||
*/
|
||||
$params['default'] = $params['default'] === true ||
|
||||
$params['default'] === Constants::SKIN_VERSION_LEGACY;
|
||||
|
||||
parent::__construct( $params );
|
||||
|
||||
$this->checkField = new \HTMLCheckField( $params );
|
||||
}
|
||||
|
||||
// BEGIN ADAPTER
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getInputHTML( $value ) {
|
||||
return $this->checkField->getInputHTML( $value === Constants::SKIN_VERSION_LEGACY );
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getInputOOUI( $value ) {
|
||||
return $this->checkField->getInputOOUI( (string)( $value === Constants::SKIN_VERSION_LEGACY ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @return string If the checkbox is checked, then `Constants::SKIN_VERSION_LEGACY`;
|
||||
* `Constants::SKIN_VERSION_LATEST` otherwise
|
||||
*/
|
||||
public function loadDataFromRequest( $request ) {
|
||||
return $this->checkField->loadDataFromRequest( $request )
|
||||
? Constants::SKIN_VERSION_LEGACY
|
||||
: Constants::SKIN_VERSION_LATEST;
|
||||
}
|
||||
|
||||
// END ADAPTER
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getLabel() {
|
||||
return $this->checkField->getLabel();
|
||||
}
|
||||
|
||||
// Note well that we can't invoke the following methods of `HTMLCheckField` directly because
|
||||
// they're protected and `HTMLSkinVectorField` doesn't extend `HTMLCheckField`.
|
||||
|
||||
/** @inheritDoc */
|
||||
protected function getLabelAlignOOUI() {
|
||||
// See \HTMLCheckField::getLabelAlignOOUI
|
||||
return 'inline';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
protected function needsLabel() {
|
||||
// See \HTMLCheckField::needsLabel
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
namespace Vector;
|
||||
|
||||
use Config;
|
||||
use HTMLForm;
|
||||
use MediaWiki\MediaWikiServices;
|
||||
use OutputPage;
|
||||
use ResourceLoaderContext;
|
||||
|
@ -11,6 +12,7 @@ use Skin;
|
|||
use SkinTemplate;
|
||||
use Title;
|
||||
use User;
|
||||
use Vector\HTMLForm\Fields\HTMLLegacySkinVersionField;
|
||||
|
||||
/**
|
||||
* Presentation hook handlers for Vector skin.
|
||||
|
@ -417,6 +419,70 @@ class Hooks {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Vector preferences to the user's Special:Preferences page directly underneath skins
|
||||
* provided that $wgVectorSkinMigrationMode is not enabled.
|
||||
*
|
||||
* @param User $user User whose preferences are being modified.
|
||||
* @param array[] &$prefs Preferences description array, to be fed to a HTMLForm object.
|
||||
*/
|
||||
public static function onGetPreferences( User $user, array &$prefs ) {
|
||||
if ( !self::getConfig( Constants::CONFIG_KEY_SHOW_SKIN_PREFERENCES ) ) {
|
||||
// Do not add Vector skin specific preferences.
|
||||
return;
|
||||
}
|
||||
|
||||
// If migration mode was enabled, and the skin version is set to modern,
|
||||
// switch over the skin.
|
||||
if ( self::isMigrationMode() && !self::isSkinVersionLegacy() ) {
|
||||
MediaWikiServices::getInstance()->getUserOptionsManager()->setOption(
|
||||
$user,
|
||||
Constants::PREF_KEY_SKIN,
|
||||
Constants::SKIN_NAME_MODERN
|
||||
);
|
||||
}
|
||||
|
||||
// Preferences to add.
|
||||
$vectorPrefs = [
|
||||
Constants::PREF_KEY_SKIN_VERSION => [
|
||||
'class' => HTMLLegacySkinVersionField::class,
|
||||
// The checkbox title.
|
||||
'label-message' => 'prefs-vector-enable-vector-1-label',
|
||||
// Show a little informational snippet underneath the checkbox.
|
||||
'help-message' => 'prefs-vector-enable-vector-1-help',
|
||||
// The tab location and title of the section to insert the checkbox. The bit after the slash
|
||||
// indicates that a prefs-skin-prefs string will be provided.
|
||||
'section' => 'rendering/skin/skin-prefs',
|
||||
'default' => self::isSkinVersionLegacy(),
|
||||
// Only show this section when the Vector skin is checked. The JavaScript client also uses
|
||||
// this state to determine whether to show or hide the whole section.
|
||||
// If migration mode is enabled, the section is always hidden.
|
||||
'hide-if' => self::isMigrationMode() ? [ '!==', 'skin', '0' ] :
|
||||
[ '!==', 'skin', Constants::SKIN_NAME_LEGACY ],
|
||||
],
|
||||
Constants::PREF_KEY_SIDEBAR_VISIBLE => [
|
||||
'type' => 'api',
|
||||
'default' => self::getConfig( Constants::CONFIG_KEY_DEFAULT_SIDEBAR_VISIBLE_FOR_AUTHORISED_USER )
|
||||
],
|
||||
];
|
||||
|
||||
// Seek the skin preference section to add Vector preferences just below it.
|
||||
$skinSectionIndex = array_search(
|
||||
Constants::PREF_KEY_SKIN, array_keys( $prefs )
|
||||
);
|
||||
if ( $skinSectionIndex !== false ) {
|
||||
// Skin preference section found. Inject Vector skin-specific preferences just below it.
|
||||
// This pattern can be found in Popups too. See T246162.
|
||||
$vectorSectionIndex = $skinSectionIndex + 1;
|
||||
$prefs = array_slice( $prefs, 0, $vectorSectionIndex, true )
|
||||
+ $vectorPrefs
|
||||
+ array_slice( $prefs, $vectorSectionIndex, null, true );
|
||||
} else {
|
||||
// Skin preference section not found. Just append Vector skin-specific preferences.
|
||||
$prefs += $vectorPrefs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds MediaWiki:Vector.css as the skin style that controls classic Vector.
|
||||
*
|
||||
|
@ -441,6 +507,62 @@ class Hooks {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook executed on user's Special:Preferences form save. This is used to convert the boolean
|
||||
* presentation of skin version to a version string. That is, a single preference change by the
|
||||
* user may trigger two writes: a boolean followed by a string.
|
||||
*
|
||||
* @param array &$formData Form data submitted by user
|
||||
* @param HTMLForm $form A preferences form
|
||||
* @param User $user Logged-in user
|
||||
* @param bool &$result Variable defining is form save successful
|
||||
* @param array $oldPreferences
|
||||
*/
|
||||
public static function onPreferencesFormPreSave(
|
||||
array &$formData,
|
||||
HTMLForm $form,
|
||||
User $user,
|
||||
&$result,
|
||||
$oldPreferences
|
||||
) {
|
||||
$userManager = MediaWikiServices::getInstance()->getUserOptionsManager();
|
||||
$skinVersion = $formData[ Constants::PREF_KEY_SKIN_VERSION ] ?? '';
|
||||
$skin = $formData[ Constants::PREF_KEY_SKIN ] ?? '';
|
||||
$isVectorEnabled = self::isVectorSkin( $skin );
|
||||
|
||||
if (
|
||||
self::isMigrationMode() &&
|
||||
$skin === Constants::SKIN_NAME_LEGACY &&
|
||||
$skinVersion === Constants::SKIN_VERSION_LATEST
|
||||
) {
|
||||
// Mismatch between skin and version. Use skin.
|
||||
$userManager->setOption(
|
||||
$user,
|
||||
Constants::PREF_KEY_SKIN_VERSION,
|
||||
Constants::SKIN_VERSION_LEGACY
|
||||
);
|
||||
}
|
||||
|
||||
if ( !$isVectorEnabled && array_key_exists( Constants::PREF_KEY_SKIN_VERSION, $oldPreferences ) ) {
|
||||
// The setting was cleared. However, this is likely because a different skin was chosen and
|
||||
// the skin version preference was hidden.
|
||||
$userManager->setOption(
|
||||
$user,
|
||||
Constants::PREF_KEY_SKIN_VERSION,
|
||||
$oldPreferences[ Constants::PREF_KEY_SKIN_VERSION ]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we can start migrating users to use skin preference.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function isMigrationMode(): bool {
|
||||
return self::getConfig( 'VectorSkinMigrationMode' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Called one time when initializing a users preferences for a newly created account.
|
||||
*
|
||||
|
@ -450,12 +572,23 @@ class Hooks {
|
|||
public static function onLocalUserCreated( User $user, $isAutoCreated ) {
|
||||
$default = self::getConfig( Constants::CONFIG_KEY_DEFAULT_SKIN_VERSION_FOR_NEW_ACCOUNTS );
|
||||
$optionsManager = MediaWikiServices::getInstance()->getUserOptionsManager();
|
||||
// Permanently set the default preference. The user can later change this preference, however,
|
||||
// self::onLocalUserCreated() will not be executed for that account again.
|
||||
$optionsManager->setOption(
|
||||
$user,
|
||||
Constants::PREF_KEY_SKIN,
|
||||
$default === Constants::SKIN_VERSION_LEGACY ?
|
||||
Constants::SKIN_NAME_LEGACY : Constants::SKIN_NAME_MODERN
|
||||
Constants::PREF_KEY_SKIN_VERSION,
|
||||
$default
|
||||
);
|
||||
|
||||
// Also set the skin key if migration mode is enabled.
|
||||
if ( self::isMigrationMode() ) {
|
||||
$optionsManager->setOption(
|
||||
$user,
|
||||
Constants::PREF_KEY_SKIN,
|
||||
$default === Constants::SKIN_VERSION_LEGACY ?
|
||||
Constants::SKIN_NAME_LEGACY : Constants::SKIN_NAME_MODERN
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -598,7 +731,7 @@ class Hooks {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get a configuration variable.
|
||||
* Get a configuration variable such as `Constants::CONFIG_KEY_SHOW_SKIN_PREFERENCES`.
|
||||
*
|
||||
* @param string $name Name of configuration option.
|
||||
* @return mixed Value configured.
|
||||
|
@ -624,7 +757,7 @@ class Hooks {
|
|||
* @param string $skinName hint that can be used to detect modern vector.
|
||||
* @return bool
|
||||
*/
|
||||
private static function isSkinVersionLegacy( $skinName ): bool {
|
||||
private static function isSkinVersionLegacy( $skinName = '' ): bool {
|
||||
if ( $skinName === Constants::SKIN_NAME_MODERN ) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ use Vector\FeatureManagement\FeatureManager;
|
|||
use Vector\FeatureManagement\Requirements\DynamicConfigRequirement;
|
||||
use Vector\FeatureManagement\Requirements\LatestSkinVersionRequirement;
|
||||
use Vector\FeatureManagement\Requirements\OverridableConfigRequirement;
|
||||
use Vector\SkinVersionLookup;
|
||||
|
||||
return [
|
||||
Constants::SERVICE_CONFIG => static function ( MediaWikiServices $services ) {
|
||||
|
@ -50,9 +51,12 @@ return [
|
|||
|
||||
$featureManager->registerRequirement(
|
||||
new LatestSkinVersionRequirement(
|
||||
$context->getRequest(),
|
||||
$context->getUser(),
|
||||
$services->getUserOptionsLookup()
|
||||
new SkinVersionLookup(
|
||||
$context->getRequest(),
|
||||
$context->getUser(),
|
||||
$services->getService( Constants::SERVICE_CONFIG ),
|
||||
$services->getUserOptionsLookup()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -193,7 +197,6 @@ return [
|
|||
Constants::FEATURE_TABLE_OF_CONTENTS,
|
||||
[
|
||||
Constants::REQUIREMENT_FULLY_INITIALISED,
|
||||
Constants::REQUIREMENT_LATEST_SKIN_VERSION,
|
||||
Constants::REQUIREMENT_TABLE_OF_CONTENTS
|
||||
]
|
||||
);
|
||||
|
|
|
@ -37,7 +37,6 @@ use Title;
|
|||
|
||||
/**
|
||||
* Skin subclass for Vector that may be the new or old version of Vector.
|
||||
* IMPORTANT: DO NOT put new code here.
|
||||
*
|
||||
* @ingroup Skins
|
||||
* Skins extending SkinVector are not supported
|
||||
|
@ -45,12 +44,33 @@ use Title;
|
|||
* @package Vector
|
||||
* @internal
|
||||
*
|
||||
* @todo
|
||||
* # Migration Plan (please remove stages when done)
|
||||
*
|
||||
* Stage 1:
|
||||
* In future when we are ready to transition to two separate skins in this order:
|
||||
* - Use $wgSkipSkins to hide vector-2022.
|
||||
* - Remove skippable field from the `vector-2022` skin version. This will defer the code to the
|
||||
* configuration option wgSkipSkins
|
||||
* - Set $wgVectorSkinMigrationMode = true and unset the Vector entry in wgSkipSkins
|
||||
* - for one wiki, to trial run. This will expose Vector in preferences. The new Vector will show
|
||||
* as Vector (2022) to begin with and the skin version preference will be hidden.
|
||||
* - Check VectorPrefDiffInstrumentation instrumentation is still working.
|
||||
*
|
||||
* Stage 2:
|
||||
* - Set $wgVectorSkinMigrationMode = true for all wikis and update skin preference labels
|
||||
* (See Iebe60b560069c8cfcdeed3f5986b8be35501dcbc). This will hide the skin version
|
||||
* preference, and update the skin preference instead.
|
||||
* - We will set $wgDefaultSkin = 'vector-2022'; for desktop improvements wikis.
|
||||
* - Run script that updates prefs table, migrating any rows where skin=vector AND
|
||||
* skinversion = 2 to skin=vector22, skinversion=2
|
||||
*
|
||||
* Stage 3:
|
||||
* - Move all modern code into SkinVector22.
|
||||
* - Move legacy skin code from SkinVector to SkinVectorLegacy.
|
||||
* - Update skin.json `vector` key to point to SkinVectorLegacy.
|
||||
* - SkinVector left as alias if necessary.
|
||||
*/
|
||||
abstract class SkinVector extends SkinMustache {
|
||||
class SkinVector extends SkinMustache {
|
||||
/** @var null|array for caching purposes */
|
||||
private $languages;
|
||||
/** @var int */
|
||||
|
@ -153,7 +173,22 @@ abstract class SkinVector extends SkinMustache {
|
|||
return '';
|
||||
}
|
||||
|
||||
abstract protected function isLegacy(): bool;
|
||||
/**
|
||||
* Whether the legacy version of the skin is being used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isLegacy(): bool {
|
||||
$options = $this->getOptions();
|
||||
if ( $options['name'] === Constants::SKIN_NAME_MODERN ) {
|
||||
return false;
|
||||
}
|
||||
$isLatestSkinFeatureEnabled = MediaWikiServices::getInstance()
|
||||
->getService( Constants::SERVICE_FEATURE_MANAGER )
|
||||
->isFeatureEnabled( Constants::FEATURE_LATEST_SKIN );
|
||||
|
||||
return !$isLatestSkinFeatureEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls getLanguages with caching.
|
||||
|
@ -390,6 +425,36 @@ abstract class SkinVector extends SkinMustache {
|
|||
Hooks::onSkinTemplateNavigation( $skin, $content_navigation );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates modules for use in legacy Vector skin.
|
||||
* Do not repeat this pattern. Will be addressed in T291098.
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getDefaultModules() {
|
||||
// FIXME: Do not repeat this pattern. Will be addressed in T291098.
|
||||
if ( $this->isLegacy() ) {
|
||||
$this->options['scripts'] = SkinVectorLegacy::getScriptsOption();
|
||||
$this->options['styles'] = SkinVectorLegacy::getStylesOption();
|
||||
} else {
|
||||
$this->options['scripts'] = SkinVector22::getScriptsOption();
|
||||
$this->options['styles'] = SkinVector22::getStylesOption();
|
||||
}
|
||||
return parent::getDefaultModules();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates HTML generation for use in legacy Vector skin.
|
||||
* Do not repeat this pattern. Will be addressed in T291098.
|
||||
*
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function generateHTML() {
|
||||
if ( $this->isLegacy() ) {
|
||||
$this->options['template'] = SkinVectorLegacy::getTemplateOption();
|
||||
}
|
||||
return parent::generateHTML();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
@ -560,14 +625,16 @@ abstract class SkinVector extends SkinMustache {
|
|||
] );
|
||||
|
||||
if ( $skin->getUser()->isRegistered() ) {
|
||||
$migrationMode = $this->getConfig()->get( 'VectorSkinMigrationMode' );
|
||||
$query = $migrationMode ? 'useskin=vector&' : '';
|
||||
// Note: This data is also passed to legacy template where it is unused.
|
||||
$optOutUrl = [
|
||||
'text' => $this->msg( 'vector-opt-out' )->text(),
|
||||
'href' => SpecialPage::getTitleFor(
|
||||
'Preferences',
|
||||
false,
|
||||
'mw-prefsection-rendering-skin'
|
||||
)->getLinkURL( 'useskin=vector&wprov=' . self::OPT_OUT_LINK_TRACKING_CODE ),
|
||||
$migrationMode ? 'mw-prefsection-rendering-skin' : 'mw-prefsection-rendering-skin-skin-prefs'
|
||||
)->getLinkURL( $query . 'wprov=' . self::OPT_OUT_LINK_TRACKING_CODE ),
|
||||
'title' => $this->msg( 'vector-opt-out-tooltip' )->text(),
|
||||
'active' => false,
|
||||
];
|
||||
|
|
|
@ -9,12 +9,16 @@ namespace Vector;
|
|||
*/
|
||||
class SkinVector22 extends SkinVector {
|
||||
/**
|
||||
* Updates the constructor to conditionally disable table of contents in article
|
||||
* body. Note, the constructor can only check feature flags that do not vary on
|
||||
* whether the user is logged in e.g. features with the 'default' key set.
|
||||
* @inheritDoc
|
||||
* Updates the constructor to conditionally disable table of contents in article body.
|
||||
*/
|
||||
public function __construct( array $options ) {
|
||||
public function __construct( $options = [] ) {
|
||||
$options += [
|
||||
'template' => self::getTemplateOption(),
|
||||
'scripts' => self::getScriptsOption(),
|
||||
'styles' => self::getStylesOption(),
|
||||
];
|
||||
|
||||
$options['toc'] = !$this->isTableOfContentsVisibleInSidebar();
|
||||
parent::__construct( $options );
|
||||
}
|
||||
|
@ -35,12 +39,40 @@ class SkinVector22 extends SkinVector {
|
|||
}
|
||||
|
||||
/**
|
||||
* Temporary function while we deprecate SkinVector class.
|
||||
* Temporary static function while we deprecate SkinVector class.
|
||||
*
|
||||
* @return bool
|
||||
* @return string
|
||||
*/
|
||||
protected function isLegacy(): bool {
|
||||
return false;
|
||||
public static function getTemplateOption() {
|
||||
return 'skin';
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary static function while we deprecate SkinVector class.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getScriptsOption() {
|
||||
return [
|
||||
'skins.vector.user',
|
||||
'skins.vector.js',
|
||||
'skins.vector.es6',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary static function while we deprecate SkinVector class.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getStylesOption() {
|
||||
return [
|
||||
'mediawiki.ui.button',
|
||||
'skins.vector.styles',
|
||||
'skins.vector.user.styles',
|
||||
'skins.vector.icons',
|
||||
'mediawiki.ui.icon',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,11 +9,45 @@ namespace Vector;
|
|||
*/
|
||||
class SkinVectorLegacy extends SkinVector {
|
||||
/**
|
||||
* Whether or not the legacy version of the skin is being used.
|
||||
*
|
||||
* @return bool
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function isLegacy(): bool {
|
||||
return true;
|
||||
public function __construct( $options = [] ) {
|
||||
$options += [
|
||||
'template' => self::getTemplateOption(),
|
||||
'scripts' => self::getScriptsOption(),
|
||||
'styles' => self::getStylesOption(),
|
||||
];
|
||||
parent::__construct( $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary static function while we deprecate SkinVector class.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getTemplateOption() {
|
||||
return 'skin-legacy';
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary static function while we deprecate SkinVector class.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getScriptsOption() {
|
||||
return [
|
||||
'skins.vector.legacy.js',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary static function while we deprecate SkinVector class.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getStylesOption() {
|
||||
return [
|
||||
'skins.vector.styles.legacy',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @since 1.35
|
||||
*/
|
||||
|
||||
namespace Vector;
|
||||
|
||||
use Config;
|
||||
use MediaWiki\User\UserOptionsLookup;
|
||||
use User;
|
||||
use WebRequest;
|
||||
|
||||
/**
|
||||
* Given initial dependencies, retrieve the current skin version. This class does no parsing, just
|
||||
* the lookup.
|
||||
*
|
||||
* Skin version is evaluated in the following order:
|
||||
*
|
||||
* - useskinversion URL query parameter override. See readme.
|
||||
*
|
||||
* - User preference. The User object for new accounts is updated (persisted as a user preference)
|
||||
* by hook according to VectorDefaultSkinVersionForNewAccounts. See Hooks and skin.json. The user
|
||||
* may then change the preference at will.
|
||||
*
|
||||
* - Site configuration default. The default is controlled by VectorDefaultSkinVersion and
|
||||
* VectorDefaultSkinVersionForExistingAccounts based on login state. The former is used
|
||||
* for anonymous users and as a fallback configuration, the latter is logged in users (existing
|
||||
* accounts). See skin.json.
|
||||
*
|
||||
* @unstable
|
||||
*
|
||||
* @package Vector
|
||||
* @internal
|
||||
*/
|
||||
final class SkinVersionLookup {
|
||||
/**
|
||||
* @var WebRequest
|
||||
*/
|
||||
private $request;
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
private $user;
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
private $config;
|
||||
/**
|
||||
* @var UserOptionsLookup
|
||||
*/
|
||||
private $userOptionsLookup;
|
||||
|
||||
/**
|
||||
* This constructor accepts all dependencies needed to obtain the skin version. The dependencies
|
||||
* are lazily evaluated, not cached, meaning they always return the current results.
|
||||
*
|
||||
* @param WebRequest $request
|
||||
* @param User $user
|
||||
* @param Config $config
|
||||
* @param UserOptionsLookup $userOptionsLookup
|
||||
*/
|
||||
public function __construct(
|
||||
WebRequest $request,
|
||||
User $user,
|
||||
Config $config,
|
||||
UserOptionsLookup $userOptionsLookup
|
||||
) {
|
||||
$this->request = $request;
|
||||
$this->user = $user;
|
||||
$this->config = $config;
|
||||
$this->userOptionsLookup = $userOptionsLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the legacy skin is being used.
|
||||
*
|
||||
* @return bool
|
||||
* @throws \ConfigException
|
||||
*/
|
||||
public function isLegacy(): bool {
|
||||
return $this->getVersion() === Constants::SKIN_VERSION_LEGACY;
|
||||
}
|
||||
|
||||
/**
|
||||
* The skin version as a string. E.g., `Constants::SKIN_VERSION_LATEST`,
|
||||
* `Constants::SKIN_VERSION_LATEST`, or maybe 'beta'. Note: it's likely someone will put arbitrary
|
||||
* strings in the query parameter which means this function returns those strings as is.
|
||||
*
|
||||
* @return string
|
||||
* @throws \ConfigException
|
||||
*/
|
||||
public function getVersion(): string {
|
||||
$migrationMode = $this->config->get( 'VectorSkinMigrationMode' );
|
||||
$useSkin = $this->request->getVal(
|
||||
Constants::QUERY_PARAM_SKIN
|
||||
);
|
||||
// In migration mode, the useskin parameter is the source of truth.
|
||||
if ( $migrationMode ) {
|
||||
if ( $useSkin ) {
|
||||
return $useSkin === Constants::SKIN_NAME_LEGACY ?
|
||||
Constants::SKIN_VERSION_LEGACY :
|
||||
Constants::SKIN_VERSION_LATEST;
|
||||
}
|
||||
}
|
||||
// [[phab:T299971]]
|
||||
if ( $useSkin === Constants::SKIN_NAME_MODERN ) {
|
||||
return Constants::SKIN_VERSION_LATEST;
|
||||
}
|
||||
|
||||
// If skin key is not vector, then version should be considered legacy.
|
||||
|
||||
// If skin is "Vector" invoke additional skin versioning detection.
|
||||
// Obtain the skin version from the 1) `useskinversion` URL query parameter override, 2) the
|
||||
// user preference, 3) the configured default for logged in users, 4) or the site default.
|
||||
//
|
||||
// The latter two configurations cannot be set by `Hooks::onUserGetDefaultOptions()` as user
|
||||
// sessions are unavailable at that time so it's not possible to determine whether the
|
||||
// preference is for a logged in user or an anonymous user. Since new users are known to have
|
||||
// had their user preferences initialized in `Hooks::onLocalUserCreated()`, that means all
|
||||
// subsequent requests to `User->getOption()` that do not have a preference set are either
|
||||
// existing accounts or anonymous users. Login state makes the distinction.
|
||||
$skin = $this->userOptionsLookup->getOption(
|
||||
$this->user,
|
||||
Constants::PREF_KEY_SKIN
|
||||
);
|
||||
|
||||
if ( $skin === Constants::SKIN_NAME_MODERN ) {
|
||||
return Constants::SKIN_VERSION_LATEST;
|
||||
}
|
||||
|
||||
$skinVersionPref = $this->userOptionsLookup->getOption(
|
||||
$this->user,
|
||||
Constants::PREF_KEY_SKIN_VERSION,
|
||||
$this->config->get(
|
||||
$this->user->isRegistered()
|
||||
? Constants::CONFIG_KEY_DEFAULT_SKIN_VERSION_FOR_EXISTING_ACCOUNTS
|
||||
: Constants::CONFIG_KEY_DEFAULT_SKIN_VERSION
|
||||
)
|
||||
);
|
||||
|
||||
// If we are in migration mode...
|
||||
if ( $migrationMode ) {
|
||||
// ... we must check the skin version preference for logged in users.
|
||||
// No need to check for anons as wgDefaultSkin has already been consulted at this point.
|
||||
if (
|
||||
$this->user->isRegistered() &&
|
||||
$skin === Constants::SKIN_NAME_LEGACY &&
|
||||
$skinVersionPref === Constants::SKIN_VERSION_LATEST
|
||||
) {
|
||||
return Constants::SKIN_VERSION_LATEST;
|
||||
}
|
||||
return Constants::SKIN_VERSION_LEGACY;
|
||||
}
|
||||
return (string)$this->request->getVal(
|
||||
Constants::QUERY_PARAM_SKIN_VERSION,
|
||||
$skinVersionPref
|
||||
);
|
||||
}
|
||||
}
|
38
skin.json
38
skin.json
|
@ -30,7 +30,6 @@
|
|||
{
|
||||
"name": "vector-2022",
|
||||
"templateDirectory": "includes/templates",
|
||||
"template": "skin",
|
||||
"responsive": true,
|
||||
"link": {
|
||||
"text-wrapper": {
|
||||
|
@ -41,18 +40,6 @@
|
|||
"skin-vector",
|
||||
"skin-vector-search-vue"
|
||||
],
|
||||
"scripts": [
|
||||
"skins.vector.user",
|
||||
"skins.vector.js",
|
||||
"skins.vector.es6"
|
||||
],
|
||||
"styles": [
|
||||
"mediawiki.ui.button",
|
||||
"skins.vector.styles",
|
||||
"skins.vector.user.styles",
|
||||
"skins.vector.icons",
|
||||
"mediawiki.ui.icon"
|
||||
],
|
||||
"messages": [
|
||||
"tooltip-p-logo",
|
||||
"vector-opt-out-tooltip",
|
||||
|
@ -74,12 +61,11 @@
|
|||
]
|
||||
},
|
||||
"vector": {
|
||||
"class": "Vector\\SkinVectorLegacy",
|
||||
"class": "Vector\\SkinVector",
|
||||
"@args": "See SkinVector::__construct for more detail.",
|
||||
"args": [
|
||||
{
|
||||
"name": "vector",
|
||||
"template": "skin-legacy",
|
||||
"templateDirectory": "includes/templates",
|
||||
"responsive": true,
|
||||
"link": {
|
||||
|
@ -87,12 +73,6 @@
|
|||
"tag": "span"
|
||||
}
|
||||
},
|
||||
"scripts": [
|
||||
"skins.vector.legacy.js"
|
||||
],
|
||||
"styles": [
|
||||
"skins.vector.styles.legacy"
|
||||
],
|
||||
"messages": [
|
||||
"tooltip-p-logo",
|
||||
"vector-opt-out-tooltip",
|
||||
|
@ -138,6 +118,8 @@
|
|||
"ResourceLoaderSiteModulePages": "Vector\\Hooks::onResourceLoaderSiteModulePages",
|
||||
"ResourceLoaderSiteStylesModulePages": "Vector\\Hooks::onResourceLoaderSiteStylesModulePages",
|
||||
"SkinPageReadyConfig": "Vector\\Hooks::onSkinPageReadyConfig",
|
||||
"GetPreferences": "Vector\\Hooks::onGetPreferences",
|
||||
"PreferencesFormPreSave": "Vector\\Hooks::onPreferencesFormPreSave",
|
||||
"LocalUserCreated": "Vector\\Hooks::onLocalUserCreated",
|
||||
"OutputPageBodyAttributes": "Vector\\Hooks::onOutputPageBodyAttributes",
|
||||
"MakeGlobalVariablesScript": "Vector\\Hooks::onMakeGlobalVariablesScript"
|
||||
|
@ -407,7 +389,15 @@
|
|||
},
|
||||
"VectorResponsive": {
|
||||
"value": false,
|
||||
"description": "@var boolean turn Vector-2022 into a responsive skin by applying a view port and disabling the min-width"
|
||||
"description": "@var boolean When wgVectorDefaultSkinVersion is set to 2, turn Vector into a responsive skin by applying a view port and disabling the min-width"
|
||||
},
|
||||
"VectorShowSkinPreferences": {
|
||||
"value": true,
|
||||
"description": "@var boolean Show skin-specific user preferences on the Special:Preferences appearance tab when true and hide them otherwise."
|
||||
},
|
||||
"VectorDefaultSkinVersion": {
|
||||
"value": "1",
|
||||
"description": "@var string:['2'|'1'] The version ('2' for latest, '1' for legacy) of the Vector skin to use for anonymous users and as a fallback. The value is _not_ persisted."
|
||||
},
|
||||
"VectorDefaultSkinVersionForExistingAccounts": {
|
||||
"value": "1",
|
||||
|
@ -417,6 +407,10 @@
|
|||
"value": "1",
|
||||
"description": "@var string:['2'|'1'] The version ('2' for latest, '1' for legacy) of the Vector skin to **set** for newly created user accounts. **The value is persisted as a user preference.** This configuration is not used for preexisting accounts (see VectorDefaultSkinVersionForExistingAccounts) and only ever executed once at new account creation time."
|
||||
},
|
||||
"VectorSkinMigrationMode": {
|
||||
"value": true,
|
||||
"description": "@internal. For usage to fulfil [[phab:T291098]]"
|
||||
},
|
||||
"VectorWvuiSearchOptions": {
|
||||
"value": {
|
||||
"showThumbnail": true,
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Skins\Vector\Tests\Integration;
|
||||
|
||||
use Vector\Constants;
|
||||
use Vector\HTMLForm\Fields\HTMLLegacySkinVersionField;
|
||||
|
||||
/**
|
||||
* @group Vector
|
||||
* @coversDefaultClass \Vector\HTMLForm\Fields\HTMLLegacySkinVersionField
|
||||
*/
|
||||
class HTMLLegacySkinVersionFieldTest extends \MediaWikiIntegrationTestCase {
|
||||
|
||||
public function provideDefault() {
|
||||
return [
|
||||
[ false, '0' ],
|
||||
[ false, false ],
|
||||
[ true, '1' ],
|
||||
[ true, true ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideDefault
|
||||
* @covers ::__construct
|
||||
*/
|
||||
public function testConstructValidatesDefault( $expected, $default ) {
|
||||
$field = new HTMLLegacySkinVersionField( [
|
||||
'default' => $default,
|
||||
'fieldname' => 'VectorSkinVersion',
|
||||
] );
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$field->getDefault()
|
||||
);
|
||||
}
|
||||
|
||||
public function provideGetInput() {
|
||||
yield [ Constants::SKIN_VERSION_LEGACY, true ];
|
||||
yield [ Constants::SKIN_VERSION_LATEST, false ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideGetInput
|
||||
* @covers ::getInputHTML
|
||||
* @covers ::getInputOOUI
|
||||
*/
|
||||
public function testGetInput( $skinVersionValue, $checkValue ) {
|
||||
$params = [
|
||||
'fieldname' => 'VectorSkinVersion',
|
||||
'class' => HTMLLegacySkinVersionField::class,
|
||||
'section' => 'rendering/skin/skin-prefs',
|
||||
'label-message' => 'prefs-vector-enable-vector-1-label',
|
||||
'help-message' => 'prefs-vector-enable-vector-1-help',
|
||||
'default' => true,
|
||||
'hide-if' => [ '!==', 'skin', Constants::SKIN_NAME_LEGACY ],
|
||||
];
|
||||
$skinVersionField = new HTMLLegacySkinVersionField( $params );
|
||||
$checkField = new \HTMLCheckField( $params );
|
||||
|
||||
$this->assertSame(
|
||||
$skinVersionField->getInputHTML( $skinVersionValue ),
|
||||
$checkField->getInputHTML( $checkValue ),
|
||||
'::getInputHTML matches HTMLCheckField::getInputHTML with mapped value'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$skinVersionField->getInputOOUI( $skinVersionValue ),
|
||||
$checkField->getInputOOUI( $checkValue ),
|
||||
'::getInputOOUI matches HTMLCheckField::getInputOOUI with mapped value'
|
||||
);
|
||||
}
|
||||
|
||||
public function provideLoadDataFromRequest() {
|
||||
// User just load the form, fallback to default.
|
||||
yield [ null, false, Constants::SKIN_VERSION_LEGACY ];
|
||||
// User submit the form, load from request.
|
||||
yield [ null, true, Constants::SKIN_VERSION_LATEST ];
|
||||
yield [ true, true, Constants::SKIN_VERSION_LEGACY ];
|
||||
// In normal request you can't get the 'false' value though.
|
||||
yield [ false, true, Constants::SKIN_VERSION_LATEST ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideLoadDataFromRequest
|
||||
* @covers ::loadDataFromRequest
|
||||
*/
|
||||
public function testLoadDataFromRequest( $wpVectorSkinVersion, $wasPosted, $expectedResult ) {
|
||||
$skinVerionField = new HTMLLegacySkinVersionField( [
|
||||
'fieldname' => 'VectorSkinVersion',
|
||||
'default' => true,
|
||||
] );
|
||||
|
||||
$requestData = [ 'wpVectorSkinVersion' => $wpVectorSkinVersion ];
|
||||
if ( $wasPosted ) {
|
||||
// Check field would load data from requst if it pass the isSubmitAttempt() check.
|
||||
$requestData['wpEditToken'] = 'ABC123';
|
||||
}
|
||||
$request = new \FauxRequest( $requestData, $wasPosted );
|
||||
|
||||
$this->assertSame( $expectedResult, $skinVerionField->loadDataFromRequest( $request ) );
|
||||
}
|
||||
}
|
|
@ -3,13 +3,11 @@ namespace MediaWiki\Skins\Vector\Tests\Integration;
|
|||
|
||||
use Exception;
|
||||
use HashConfig;
|
||||
use MediaWiki\MediaWikiServices;
|
||||
use MediaWikiIntegrationTestCase;
|
||||
use ReflectionMethod;
|
||||
use RequestContext;
|
||||
use Title;
|
||||
use Vector\SkinVector22;
|
||||
use Vector\SkinVectorLegacy;
|
||||
use Vector\SkinVector;
|
||||
use Wikimedia\TestingAccessWrapper;
|
||||
|
||||
/**
|
||||
|
@ -17,16 +15,16 @@ use Wikimedia\TestingAccessWrapper;
|
|||
* @package MediaWiki\Skins\Vector\Tests\Unit
|
||||
* @group Vector
|
||||
* @group Skins
|
||||
*
|
||||
* @coversDefaultClass \Vector\SkinVector
|
||||
*/
|
||||
class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
||||
|
||||
/**
|
||||
* @return SkinVectorLegacy
|
||||
* @return SkinVector
|
||||
*/
|
||||
private function provideVectorTemplateObject() {
|
||||
$skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
|
||||
$template = $skinFactory->makeSkin( 'vector' );
|
||||
return $template;
|
||||
return new SkinVector( [ 'name' => 'vector' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,7 +116,7 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* @covers \Vector\SkinVector::getTocData
|
||||
* @covers ::getTocData
|
||||
* @dataProvider provideGetTOCData
|
||||
*/
|
||||
public function testGetTocData(
|
||||
|
@ -130,7 +128,7 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
|||
'wgVectorTableOfContentsCollapseAtCount' => $configValue
|
||||
] );
|
||||
|
||||
$skinVector = new SkinVectorLegacy( [ 'name' => 'vector-2022' ] );
|
||||
$skinVector = new SkinVector( [ 'name' => 'vector-2022' ] );
|
||||
$openSkinVector = TestingAccessWrapper::newFromObject( $skinVector );
|
||||
$data = $openSkinVector->getTocData( $tocData );
|
||||
|
||||
|
@ -148,7 +146,7 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* @covers \Vector\SkinVector::getTemplateData
|
||||
* @covers ::getTemplateData
|
||||
*/
|
||||
public function testGetTemplateData() {
|
||||
$title = Title::newFromText( 'SkinVector' );
|
||||
|
@ -372,7 +370,7 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
|||
|
||||
/**
|
||||
* @dataProvider providerLanguageAlertRequirements
|
||||
* @covers \Vector\SkinVector::shouldLanguageAlertBeInSidebar
|
||||
* @covers ::shouldLanguageAlertBeInSidebar
|
||||
* @param array $requirements
|
||||
* @param Title $title
|
||||
* @param array $getLanguagesCached
|
||||
|
@ -396,7 +394,7 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
|||
] ) );
|
||||
$this->installMockMwServices( $config );
|
||||
|
||||
$mockSkinVector = $this->getMockBuilder( SkinVector22::class )
|
||||
$mockSkinVector = $this->getMockBuilder( SkinVector::class )
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods( [ 'getTitle', 'getLanguagesCached','isLanguagesInContentAt', 'shouldHideLanguages' ] )
|
||||
->getMock();
|
||||
|
@ -410,7 +408,7 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
|||
->willReturn( $shouldHideLanguages );
|
||||
|
||||
$shouldLanguageAlertBeInSidebarMethod = new ReflectionMethod(
|
||||
SkinVector22::class,
|
||||
SkinVector::class,
|
||||
'shouldLanguageAlertBeInSidebar'
|
||||
);
|
||||
$shouldLanguageAlertBeInSidebarMethod->setAccessible( true );
|
||||
|
|
|
@ -0,0 +1,368 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @since 1.35
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Skins\Vector\Tests\Integration;
|
||||
|
||||
use HashConfig;
|
||||
use MediaWiki\User\UserOptionsLookup;
|
||||
use Vector\Constants;
|
||||
use Vector\SkinVersionLookup;
|
||||
|
||||
/**
|
||||
* @group Vector
|
||||
* @coversDefaultClass \Vector\SkinVersionLookup
|
||||
*/
|
||||
class SkinVersionLookupTest extends \MediaWikiIntegrationTestCase {
|
||||
/**
|
||||
* @covers ::isLegacy
|
||||
* @covers ::getVersion
|
||||
*/
|
||||
public function testRequest() {
|
||||
$request = $this->getMockBuilder( \WebRequest::class )->getMock();
|
||||
$request
|
||||
->method( 'getVal' )
|
||||
->willReturnCallback( static function ( $key ) {
|
||||
if ( $key === Constants::QUERY_PARAM_SKIN ) {
|
||||
return null;
|
||||
} else {
|
||||
return 'alpha';
|
||||
}
|
||||
} );
|
||||
|
||||
$user = $this->createMock( \User::class );
|
||||
$user
|
||||
->method( 'isRegistered' )
|
||||
->willReturn( false );
|
||||
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorDefaultSkinVersion' => '2',
|
||||
'VectorDefaultSkinVersionForExistingAccounts' => '1'
|
||||
] );
|
||||
|
||||
$userOptionsLookup = $this->getUserOptionsLookupMock( $user, 'beta', [
|
||||
'skin' => Constants::SKIN_NAME_LEGACY,
|
||||
] );
|
||||
|
||||
$skinVersionLookup = new SkinVersionLookup( $request, $user, $config, $userOptionsLookup );
|
||||
|
||||
$this->assertSame(
|
||||
'alpha',
|
||||
$skinVersionLookup->getVersion(),
|
||||
'Query parameter is the first priority.'
|
||||
);
|
||||
$this->assertSame(
|
||||
false,
|
||||
$skinVersionLookup->isLegacy(),
|
||||
'Version is non-Legacy.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getVersion
|
||||
* @covers ::isLegacy
|
||||
*/
|
||||
public function testUserPreference() {
|
||||
$request = $this->getMockBuilder( \WebRequest::class )->getMock();
|
||||
$request
|
||||
->method( 'getVal' )
|
||||
->willReturnCallback( static function ( $key ) {
|
||||
if ( $key === Constants::QUERY_PARAM_SKIN ) {
|
||||
return null;
|
||||
} else {
|
||||
return 'beta';
|
||||
}
|
||||
} );
|
||||
|
||||
$user = $this->createMock( \User::class );
|
||||
$user
|
||||
->method( 'isRegistered' )
|
||||
->willReturn( false );
|
||||
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorDefaultSkinVersion' => '2',
|
||||
'VectorDefaultSkinVersionForExistingAccounts' => '1'
|
||||
] );
|
||||
|
||||
$userOptionsLookup = $this->getUserOptionsLookupMock( $user, 'beta', [
|
||||
'skin' => Constants::SKIN_NAME_LEGACY,
|
||||
] );
|
||||
|
||||
$skinVersionLookup = new SkinVersionLookup( $request, $user, $config, $userOptionsLookup );
|
||||
|
||||
$this->assertSame(
|
||||
'beta',
|
||||
$skinVersionLookup->getVersion(),
|
||||
'User preference is the second priority.'
|
||||
);
|
||||
$this->assertSame(
|
||||
false,
|
||||
$skinVersionLookup->isLegacy(),
|
||||
'Version is non-Legacy.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getVersion
|
||||
* @covers ::isLegacy
|
||||
*/
|
||||
public function testConfigRegistered() {
|
||||
$request = $this->getMockBuilder( \WebRequest::class )->getMock();
|
||||
$request
|
||||
->method( 'getVal' )
|
||||
->willReturnCallback( static function ( $key ) {
|
||||
if ( $key === Constants::QUERY_PARAM_SKIN ) {
|
||||
return null;
|
||||
} else {
|
||||
return '1';
|
||||
}
|
||||
} );
|
||||
|
||||
$user = $this->createMock( \User::class );
|
||||
$user
|
||||
->method( 'isRegistered' )
|
||||
->willReturn( true );
|
||||
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorDefaultSkinVersion' => '2',
|
||||
'VectorDefaultSkinVersionForExistingAccounts' => '1'
|
||||
] );
|
||||
|
||||
$userOptionsLookup = $this->getUserOptionsLookupMock( $user, '1', [
|
||||
'skin' => Constants::SKIN_NAME_LEGACY,
|
||||
] );
|
||||
|
||||
$skinVersionLookup = new SkinVersionLookup( $request, $user, $config, $userOptionsLookup );
|
||||
|
||||
$this->assertSame(
|
||||
'1',
|
||||
$skinVersionLookup->getVersion(),
|
||||
'Config is the third priority and distinguishes logged in users from anonymous users.'
|
||||
);
|
||||
$this->assertSame(
|
||||
true,
|
||||
$skinVersionLookup->isLegacy(),
|
||||
'Version is Legacy.'
|
||||
);
|
||||
}
|
||||
|
||||
public function providerAnonUserMigrationMode() {
|
||||
return [
|
||||
// When no query string just return DefaultSkin version.
|
||||
[
|
||||
Constants::SKIN_NAME_LEGACY,
|
||||
null,
|
||||
Constants::SKIN_VERSION_LEGACY,
|
||||
],
|
||||
[
|
||||
Constants::SKIN_NAME_MODERN,
|
||||
null,
|
||||
Constants::SKIN_VERSION_LATEST,
|
||||
],
|
||||
// When useskin=vector return legacy Vector version.
|
||||
[
|
||||
Constants::SKIN_NAME_LEGACY,
|
||||
Constants::SKIN_NAME_LEGACY,
|
||||
Constants::SKIN_VERSION_LEGACY,
|
||||
],
|
||||
[
|
||||
Constants::SKIN_NAME_MODERN,
|
||||
Constants::SKIN_NAME_LEGACY,
|
||||
Constants::SKIN_VERSION_LEGACY,
|
||||
],
|
||||
// When useskin=vector-2022 return modern Vector.
|
||||
[
|
||||
Constants::SKIN_NAME_MODERN,
|
||||
Constants::SKIN_NAME_MODERN,
|
||||
Constants::SKIN_VERSION_LATEST,
|
||||
],
|
||||
[
|
||||
Constants::SKIN_NAME_LEGACY,
|
||||
Constants::SKIN_NAME_MODERN,
|
||||
Constants::SKIN_VERSION_LATEST,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getVersion
|
||||
* @dataProvider providerAnonUserMigrationMode
|
||||
*/
|
||||
public function testVectorAnonUserMigrationModeWithUseSkinVector(
|
||||
string $defaultSkin,
|
||||
$useSkin,
|
||||
$expectedVersion
|
||||
) {
|
||||
$request = $this->getMockBuilder( \WebRequest::class )->getMock();
|
||||
$request
|
||||
->method( 'getVal' )
|
||||
->with( 'useskin' )
|
||||
->willReturn( $useSkin );
|
||||
$user = $this->createMock( \User::class );
|
||||
$user
|
||||
->method( 'isRegistered' )
|
||||
->willReturn( false );
|
||||
|
||||
$config = new HashConfig( [
|
||||
'DefaultSkin' => $defaultSkin,
|
||||
'VectorSkinMigrationMode' => true,
|
||||
'VectorDefaultSkinVersion' => '2',
|
||||
'VectorDefaultSkinVersionForExistingAccounts' => '2'
|
||||
] );
|
||||
$userOptionsLookup = $this->getUserOptionsLookupMock( $user, '2', [
|
||||
'skin' => $defaultSkin,
|
||||
] );
|
||||
|
||||
$skinVersionLookup = new SkinVersionLookup( $request, $user, $config, $userOptionsLookup );
|
||||
|
||||
$this->assertSame(
|
||||
$expectedVersion,
|
||||
$skinVersionLookup->getVersion(),
|
||||
'useskin=vector query string yields legacy skin in migration mode'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getVersion
|
||||
*/
|
||||
public function testVectorRegisteredUserMigrationMode() {
|
||||
$request = $this->getMockBuilder( \WebRequest::class )->getMock();
|
||||
$request
|
||||
->method( 'getVal' )
|
||||
->willReturn( null );
|
||||
$user = $this->createMock( \User::class );
|
||||
$user
|
||||
->method( 'isRegistered' )
|
||||
->willReturn( true );
|
||||
|
||||
$config = new HashConfig( [
|
||||
'DefaultSkin' => 'vector',
|
||||
'VectorSkinMigrationMode' => true,
|
||||
'VectorDefaultSkinVersion' => '1',
|
||||
'VectorDefaultSkinVersionForExistingAccounts' => '1'
|
||||
] );
|
||||
$userOptionsLookup = $this->getUserOptionsLookupMock( $user, '2', [
|
||||
'skin' => Constants::SKIN_NAME_LEGACY
|
||||
] );
|
||||
|
||||
$skinVersionLookup = new SkinVersionLookup( $request, $user, $config, $userOptionsLookup );
|
||||
|
||||
$this->assertSame(
|
||||
'2',
|
||||
$skinVersionLookup->getVersion(),
|
||||
'If legacy skin is set with skin version modern, then the user gets modern skin still'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getVersion
|
||||
*/
|
||||
public function testSkin22() {
|
||||
$request = $this->getMockBuilder( \WebRequest::class )->getMock();
|
||||
$request
|
||||
->method( 'getVal' )
|
||||
->willReturn( '1' );
|
||||
$user = $this->createMock( \User::class );
|
||||
$user
|
||||
->method( 'isRegistered' )
|
||||
->willReturn( true );
|
||||
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorDefaultSkinVersion' => '1',
|
||||
'VectorDefaultSkinVersionForExistingAccounts' => '1'
|
||||
] );
|
||||
|
||||
$userOptionsLookup = $this->getUserOptionsLookupMock( $user, '1', [
|
||||
'skin' => Constants::SKIN_NAME_MODERN
|
||||
] );
|
||||
|
||||
$skinVersionLookup = new SkinVersionLookup( $request, $user, $config, $userOptionsLookup );
|
||||
|
||||
$this->assertSame(
|
||||
'2',
|
||||
$skinVersionLookup->getVersion(),
|
||||
'Using the modern skin always returns 2. Ignores skinversion query string.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getVersion
|
||||
* @covers ::isLegacy
|
||||
*/
|
||||
public function testConfigAnon() {
|
||||
$request = $this->getMockBuilder( \WebRequest::class )->getMock();
|
||||
$request
|
||||
->method( 'getVal' )
|
||||
->willReturnCallback( static function ( $key ) {
|
||||
if ( $key === Constants::QUERY_PARAM_SKIN ) {
|
||||
return null;
|
||||
} else {
|
||||
return '2';
|
||||
}
|
||||
} );
|
||||
|
||||
$user = $this->createMock( \User::class );
|
||||
$user
|
||||
->method( 'isRegistered' )
|
||||
->willReturn( false );
|
||||
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorDefaultSkinVersion' => '2',
|
||||
'VectorDefaultSkinVersionForExistingAccounts' => '1'
|
||||
] );
|
||||
|
||||
$userOptionsLookup = $this->getUserOptionsLookupMock( $user, '2', [
|
||||
'skin' => Constants::SKIN_NAME_LEGACY,
|
||||
] );
|
||||
|
||||
$skinVersionLookup = new SkinVersionLookup( $request, $user, $config, $userOptionsLookup );
|
||||
|
||||
$this->assertSame(
|
||||
'2',
|
||||
$skinVersionLookup->getVersion(),
|
||||
'Config is the third priority and distinguishes anonymous users from logged in users.'
|
||||
);
|
||||
$this->assertSame(
|
||||
false,
|
||||
$skinVersionLookup->isLegacy(),
|
||||
'Version is non-Legacy.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param array $returnVal
|
||||
* @param array $lookup values
|
||||
* @return UserOptionsLookup
|
||||
*/
|
||||
private function getUserOptionsLookupMock( $user, $returnVal, $lookup = [] ) {
|
||||
$mock = $this->createMock( UserOptionsLookup::class );
|
||||
$mock->method( 'getOption' )
|
||||
->willReturnCallback( static function ( $user, $key ) use ( $returnVal, $lookup ) {
|
||||
return $lookup[ $key ] ?? $returnVal;
|
||||
} );
|
||||
return $mock;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
namespace MediaWiki\Skins\Vector\Tests\Integration;
|
||||
|
||||
use HashConfig;
|
||||
use HTMLForm;
|
||||
use MediaWiki\User\UserOptionsManager;
|
||||
use MediaWikiIntegrationTestCase;
|
||||
use ReflectionMethod;
|
||||
|
@ -17,6 +18,8 @@ use User;
|
|||
use Vector\Constants;
|
||||
use Vector\FeatureManagement\FeatureManager;
|
||||
use Vector\Hooks;
|
||||
use Vector\HTMLForm\Fields\HTMLLegacySkinVersionField;
|
||||
use Vector\SkinVector;
|
||||
use Vector\SkinVector22;
|
||||
use Vector\SkinVectorLegacy;
|
||||
|
||||
|
@ -288,6 +291,21 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onGetPreferences
|
||||
*/
|
||||
public function testOnGetPreferencesShowPreferencesDisabled() {
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorShowSkinPreferences' => false,
|
||||
] );
|
||||
$this->setService( 'Vector.Config', $config );
|
||||
|
||||
$prefs = [];
|
||||
Hooks::onGetPreferences( $this->getTestUser()->getUser(), $prefs );
|
||||
$this->assertSame( [], $prefs, 'No preferences are added.' );
|
||||
}
|
||||
|
||||
private function setFeatureLatestSkinVersionIsEnabled( $isEnabled ) {
|
||||
$featureManager = new FeatureManager();
|
||||
$featureManager->registerSimpleRequirement( Constants::REQUIREMENT_LATEST_SKIN_VERSION, $isEnabled );
|
||||
|
@ -298,6 +316,48 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
|||
$this->setService( Constants::SERVICE_FEATURE_MANAGER, $featureManager );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onGetPreferences
|
||||
*/
|
||||
public function testOnGetPreferencesShowPreferencesEnabledSkinSectionFoundLegacy() {
|
||||
$isLegacy = true;
|
||||
$this->setFeatureLatestSkinVersionIsEnabled( !$isLegacy );
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorShowSkinPreferences' => true,
|
||||
'VectorDefaultSidebarVisibleForAuthorisedUser' => true,
|
||||
] );
|
||||
$this->setService( 'Vector.Config', $config );
|
||||
|
||||
$prefs = [
|
||||
'foo' => [],
|
||||
'skin' => [],
|
||||
'bar' => []
|
||||
];
|
||||
Hooks::onGetPreferences( $this->getTestUser()->getUser(), $prefs );
|
||||
$this->assertEquals(
|
||||
[
|
||||
'foo' => [],
|
||||
'skin' => [],
|
||||
'VectorSkinVersion' => [
|
||||
'class' => HTMLLegacySkinVersionField::class,
|
||||
'label-message' => 'prefs-vector-enable-vector-1-label',
|
||||
'help-message' => 'prefs-vector-enable-vector-1-help',
|
||||
'section' => self::SKIN_PREFS_SECTION,
|
||||
'default' => $isLegacy,
|
||||
'hide-if' => self::HIDE_IF,
|
||||
],
|
||||
'VectorSidebarVisible' => [
|
||||
'type' => 'api',
|
||||
'default' => true
|
||||
],
|
||||
'bar' => [],
|
||||
],
|
||||
$prefs,
|
||||
'Preferences are inserted directly after skin.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getVectorResourceLoaderConfig
|
||||
* @dataProvider provideGetVectorResourceLoaderConfig
|
||||
|
@ -328,11 +388,118 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onGetPreferences
|
||||
*/
|
||||
public function testOnGetPreferencesShowPreferencesEnabledSkinSectionMissingLegacy() {
|
||||
$isLegacy = false;
|
||||
$this->setFeatureLatestSkinVersionIsEnabled( !$isLegacy );
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorDefaultSidebarVisibleForAuthorisedUser' => true,
|
||||
'VectorShowSkinPreferences' => true,
|
||||
] );
|
||||
$this->setService( 'Vector.Config', $config );
|
||||
|
||||
$prefs = [
|
||||
'foo' => [],
|
||||
'bar' => []
|
||||
];
|
||||
Hooks::onGetPreferences( $this->getTestUser()->getUser(), $prefs );
|
||||
$this->assertEquals(
|
||||
[
|
||||
'foo' => [],
|
||||
'bar' => [],
|
||||
'VectorSkinVersion' => [
|
||||
'class' => HTMLLegacySkinVersionField::class,
|
||||
'label-message' => 'prefs-vector-enable-vector-1-label',
|
||||
'help-message' => 'prefs-vector-enable-vector-1-help',
|
||||
'section' => self::SKIN_PREFS_SECTION,
|
||||
'default' => $isLegacy,
|
||||
'hide-if' => self::HIDE_IF,
|
||||
],
|
||||
'VectorSidebarVisible' => [
|
||||
'type' => 'api',
|
||||
'default' => true
|
||||
],
|
||||
],
|
||||
$prefs,
|
||||
'Preferences are appended.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onPreferencesFormPreSave
|
||||
*/
|
||||
public function testOnPreferencesFormPreSaveVectorEnabledLegacyNewPreference() {
|
||||
$formData = [
|
||||
'skin' => 'vector',
|
||||
'VectorSkinVersion' => Constants::SKIN_VERSION_LEGACY,
|
||||
];
|
||||
$form = $this->createMock( HTMLForm::class );
|
||||
$user = $this->createMock( User::class );
|
||||
$userOptionsManager = $this->createMock( UserOptionsManager::class );
|
||||
$userOptionsManager->expects( $this->never() )
|
||||
->method( 'setOption' );
|
||||
$this->setService( 'UserOptionsManager', $userOptionsManager );
|
||||
$result = true;
|
||||
$oldPreferences = [];
|
||||
|
||||
Hooks::onPreferencesFormPreSave( $formData, $form, $user, $result, $oldPreferences );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onPreferencesFormPreSave
|
||||
*/
|
||||
public function testOnPreferencesFormPreSaveVectorDisabledNoOldPreference() {
|
||||
$formData = [
|
||||
'VectorSkinVersion' => Constants::SKIN_VERSION_LATEST,
|
||||
];
|
||||
$form = $this->createMock( HTMLForm::class );
|
||||
$user = $this->createMock( User::class );
|
||||
$userOptionsManager = $this->createMock( UserOptionsManager::class );
|
||||
$userOptionsManager->expects( $this->never() )
|
||||
->method( 'setOption' );
|
||||
$this->setService( 'UserOptionsManager', $userOptionsManager );
|
||||
$result = true;
|
||||
$oldPreferences = [];
|
||||
|
||||
Hooks::onPreferencesFormPreSave( $formData, $form, $user, $result, $oldPreferences );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onPreferencesFormPreSave
|
||||
*/
|
||||
public function testOnPreferencesFormPreSaveVectorDisabledOldPreference() {
|
||||
$formData = [
|
||||
'VectorSkinVersion' => Constants::SKIN_VERSION_LATEST,
|
||||
];
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorShowSkinPreferences' => false,
|
||||
] );
|
||||
$form = $this->createMock( HTMLForm::class );
|
||||
$user = $this->createMock( User::class );
|
||||
$userOptionsManager = $this->createMock( UserOptionsManager::class );
|
||||
$userOptionsManager->expects( $this->once() )
|
||||
->method( 'setOption' )
|
||||
->with( $user, 'VectorSkinVersion', 'old' );
|
||||
$this->setService( 'Vector.Config', $config );
|
||||
$this->setService( 'UserOptionsManager', $userOptionsManager );
|
||||
$result = true;
|
||||
$oldPreferences = [
|
||||
'VectorSkinVersion' => 'old',
|
||||
];
|
||||
|
||||
Hooks::onPreferencesFormPreSave( $formData, $form, $user, $result, $oldPreferences );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onLocalUserCreated
|
||||
*/
|
||||
public function testOnLocalUserCreatedLegacy() {
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorDefaultSkinVersionForNewAccounts' => Constants::SKIN_VERSION_LEGACY,
|
||||
] );
|
||||
$this->setService( 'Vector.Config', $config );
|
||||
|
@ -341,7 +508,7 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
|||
$userOptionsManager = $this->createMock( UserOptionsManager::class );
|
||||
$userOptionsManager->expects( $this->once() )
|
||||
->method( 'setOption' )
|
||||
->with( $user, 'skin', Constants::SKIN_NAME_LEGACY );
|
||||
->with( $user, 'VectorSkinVersion', Constants::SKIN_VERSION_LEGACY );
|
||||
$this->setService( 'UserOptionsManager', $userOptionsManager );
|
||||
$isAutoCreated = false;
|
||||
Hooks::onLocalUserCreated( $user, $isAutoCreated );
|
||||
|
@ -352,6 +519,7 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
|||
*/
|
||||
public function testOnLocalUserCreatedLatest() {
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorDefaultSkinVersionForNewAccounts' => Constants::SKIN_VERSION_LATEST,
|
||||
] );
|
||||
$this->setService( 'Vector.Config', $config );
|
||||
|
@ -360,7 +528,7 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
|||
$userOptionsManager = $this->createMock( UserOptionsManager::class );
|
||||
$userOptionsManager->expects( $this->once() )
|
||||
->method( 'setOption' )
|
||||
->with( $user, 'skin', Constants::SKIN_NAME_MODERN );
|
||||
->with( $user, 'VectorSkinVersion', Constants::SKIN_VERSION_LATEST );
|
||||
$this->setService( 'UserOptionsManager', $userOptionsManager );
|
||||
$isAutoCreated = false;
|
||||
Hooks::onLocalUserCreated( $user, $isAutoCreated );
|
||||
|
@ -373,7 +541,7 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
|||
$this->setMwGlobals( [
|
||||
'wgVectorUseIconWatch' => true
|
||||
] );
|
||||
$skin = new SkinVector22( [ 'name' => 'vector' ] );
|
||||
$skin = new SkinVector( [ 'name' => 'vector' ] );
|
||||
$skin->getContext()->setTitle( Title::newFromText( 'Foo' ) );
|
||||
$contentNavWatch = [
|
||||
'actions' => [
|
||||
|
|
|
@ -20,9 +20,11 @@
|
|||
|
||||
namespace Vector\FeatureManagement\Tests;
|
||||
|
||||
use HashConfig;
|
||||
use MediaWiki\User\UserOptionsLookup;
|
||||
use User;
|
||||
use Vector\FeatureManagement\Requirements\LatestSkinVersionRequirement;
|
||||
use Vector\SkinVersionLookup;
|
||||
use WebRequest;
|
||||
|
||||
/**
|
||||
|
@ -34,33 +36,38 @@ class LatestSkinVersionRequirementTest extends \MediaWikiUnitTestCase {
|
|||
|
||||
public function provideIsMet() {
|
||||
// $version, $expected, $msg
|
||||
yield 'not met' => [ 'vector', null, false, '"1" isn\'t considered latest.' ];
|
||||
yield 'met' => [ 'vector-2022', null, true, '"2" is considered latest.' ];
|
||||
yield 'met (useskin override)' => [ 'vector', 'vector-2022', true, 'useskin overrides' ];
|
||||
yield 'not met (useskin override)' => [ 'vector-2022', 'vector', false, 'useskin overrides' ];
|
||||
yield 'not met' => [ '1', false, '"1" isn\'t considered latest.' ];
|
||||
yield 'met' => [ '2', true, '"2" is considered latest.' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideIsMet
|
||||
* @covers ::isMet
|
||||
*/
|
||||
public function testIsMet( $skin, $useSkin, $expected, $msg ) {
|
||||
public function testIsMet( $version, $expected, $msg ) {
|
||||
$config = new HashConfig( [
|
||||
'VectorSkinMigrationMode' => false,
|
||||
'VectorDefaultSkinVersionForExistingAccounts' => $version
|
||||
] );
|
||||
|
||||
$user = $this->createMock( User::class );
|
||||
$user->method( 'isRegistered' )->willReturn( true );
|
||||
$user->method( 'isSafeToLoad' )->willReturn( true );
|
||||
|
||||
$userOptionsLookup = $this->createMock( UserOptionsLookup::class );
|
||||
$userOptionsLookup->method( 'getOption' )
|
||||
->willReturn( $skin );
|
||||
->will( $this->returnArgument( 2 ) );
|
||||
|
||||
$request = $this->createMock( WebRequest::class );
|
||||
$request->method( 'getVal' )
|
||||
->willReturn( $useSkin );
|
||||
->will( $this->returnArgument( 1 ) );
|
||||
|
||||
$requirement = new LatestSkinVersionRequirement(
|
||||
$request,
|
||||
$user,
|
||||
$userOptionsLookup
|
||||
new SkinVersionLookup(
|
||||
$request,
|
||||
$user,
|
||||
$config,
|
||||
$userOptionsLookup
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertSame( $expected, $requirement->isMet(), $msg );
|
||||
|
|
Loading…
Reference in New Issue