Remove custom handling of user links menu items and handle case when anon editor links are disabled

Rather than try to build individually build login, logout, and create account menu items, we handle them the same as all other user links menu items in Hooks.php. While Hooks isnt ideal, there currently isnt a good path for moving that code to SkinComponents until core provides menu data. In the meantime, this patch reduces code complexity and prevents bugs like T324638.
This approach also allows us to move user links logic from SkinVector22 to VectorComponentUserLinks.php, and ensures the user links dropdown contains multiple menus, which allows us to reuse styles from page tools dropdowns.

Expected 11 visual changes:
* minor visual change where the user links dropdown has an
additional 4px vertical padding.
This padding was originally added to the page tools dropdowns
per Alex's request, but Alex also said that dropdowns should share
the same spacing. This change makes the styles all consistent
* Order of talk and contribution links have been swapped

Bug: T289212
Bug: T319356
Bug: T328954
Change-Id: Iac0586893fec26a8a6c2c904ce08fbf1e19b339c
This commit is contained in:
bwang 2023-02-01 17:38:44 -06:00
parent f415a556dd
commit 9e4bdd00bf
14 changed files with 230 additions and 212 deletions

View file

@ -2,6 +2,7 @@
namespace MediaWiki\Skins\Vector\Components;
use Linker;
use MediaWiki\Skin\SkinComponentLink;
use MediaWiki\Skins\Vector\Constants;
use MediaWiki\Skins\Vector\VectorServices;
use Message;
@ -17,33 +18,27 @@ class VectorComponentUserLinks implements VectorComponent {
private $localizer;
/** @var User */
private $user;
/** @var VectorComponentMenu */
private $userMenu;
/** @var VectorComponentMenu */
private $overflowMenu;
/** @var VectorComponentMenu */
private $accountMenu;
/** @var array */
private $portletData;
/** @var array */
private $linkOptions;
/**
* @param MessageLocalizer $localizer
* @param User $user
* @param VectorComponentMenu $userMenu menu of icon only links
* @param VectorComponentMenu $overflowMenu menu that appears in dropdown
* @param VectorComponentMenu $accountMenu links that appear inside dropdown
* for login, logout or create account.
* @param array $portletData
* @param array $linkOptions
*/
public function __construct(
MessageLocalizer $localizer,
User $user,
VectorComponentMenu $userMenu,
VectorComponentMenu $overflowMenu,
VectorComponentMenu $accountMenu
array $portletData,
array $linkOptions
) {
$this->localizer = $localizer;
$this->user = $user;
$this->userMenu = $userMenu;
$this->overflowMenu = $overflowMenu;
$this->accountMenu = $accountMenu;
$this->portletData = $portletData;
$this->linkOptions = $linkOptions;
}
/**
@ -55,37 +50,32 @@ class VectorComponentUserLinks implements VectorComponent {
}
/**
* @inheritDoc
* @param bool $isDefaultAnonUserLinks
* @param bool $isAnonEditorLinksEnabled
* @return VectorComponentDropdown
*/
public function getTemplateData(): array {
$userMenu = $this->userMenu;
$overflowMenu = $this->overflowMenu;
private function getDropdown( $isDefaultAnonUserLinks, $isAnonEditorLinksEnabled ) {
$user = $this->user;
$isAnon = !$user->isRegistered();
$isRegisteredUser = $user->isRegistered();
// T317789: Core can undesirably add an 'emptyPortlet' class that hides the
// user menu. This is a result of us manually removing items from the menu
// in Hooks::updateUserLinksDropdownItems which can make
// SkinTemplate::getPortletData apply the `emptyPortlet` class if there are
// no menu items. Since we subsequently add menu items in
// SkinVector::getUserLinksTemplateData, the `emptyPortlet` class is
// innaccurate. This is why we add the desired classes, `mw-portlet` and
// `mw-portlet-personal` here instead. This can potentially be removed upon
// completion of T319356.
//
// Also, add target class to apply different icon to personal menu dropdown for logged in users.
$class = 'mw-portlet mw-portlet-personal vector-user-menu';
$class = 'vector-user-menu';
if ( VectorServices::getFeatureManager()->isFeatureEnabled( Constants::FEATURE_PAGE_TOOLS ) ) {
$class .= ' mw-ui-icon-flush-right';
}
$class .= $isRegisteredUser ?
$class .= !$isAnon ?
' vector-user-menu-logged-in' :
' vector-user-menu-logged-out';
// Hide entire user links dropdown on larger viewports if it only contains
// create account & login link, which are only shown on smaller viewports
if ( $isAnon && $isDefaultAnonUserLinks && !$isAnonEditorLinksEnabled ) {
$class .= ' user-links-collapsible-item';
}
$tooltip = '';
if ( $user->isTemp() ) {
$icon = 'userAnonymous';
} elseif ( $isRegisteredUser ) {
} elseif ( !$isAnon ) {
$icon = 'userAvatar';
} else {
$icon = 'ellipsis';
@ -95,39 +85,90 @@ class VectorComponentUserLinks implements VectorComponent {
// This overrides the tooltip for the user links menu icon which is an ellipsis for anonymous users.
$tooltip = Linker::tooltip( 'vector-anon-user-menu-title' ) ?? '';
}
$userMenuDropdown = new VectorComponentDropdown(
return new VectorComponentDropdown(
'p-personal', $this->msg( 'personaltools' )->text(), $class, $icon, $tooltip
);
$additionalData = [];
}
/**
* @param bool $isDefaultAnonUserLinks
* @param bool $isAnonEditorLinksEnabled
* @return array
*/
private function getDropdownMenus( $isDefaultAnonUserLinks, $isAnonEditorLinksEnabled ) {
$user = $this->user;
$isAnon = !$user->isRegistered();
$portletData = $this->portletData;
// Hide default user menu on larger viewports if it only contains
// create account & login link, which are only shown on smaller viewports
// FIXME: Replace array_merge with an add class helper function
$userMenuClass = $portletData[ 'data-user-menu' ][ 'class' ];
$userMenuClass = $isAnon && $isDefaultAnonUserLinks ?
$userMenuClass . ' user-links-collapsible-item' : $userMenuClass;
$dropdownMenus = [
new VectorComponentMenu( [
'label' => null,
'class' => $userMenuClass
] + $portletData[ 'data-user-menu' ] )
];
if ( $isAnon ) {
// T317789: The `anontalk` and `anoncontribs` links will not be added to
// the menu if `$wgGroupPermissions['*']['edit']` === false which can
// leave the menu empty due to our removal of other user menu items in
// `Hooks::updateUserLinksDropdownItems`. In this case, we do not want
// to render the anon "learn more" link.
if ( $isAnon && count( $userMenu ) > 0 ) {
$learnMoreLink = new VectorComponentIconLink(
Title::newFromText( $this->msg( 'vector-intro-page' )->text() )->getLocalURL(),
$this->msg( 'vector-anon-user-menu-pages-learn' )->text(),
null,
$this->localizer,
'vector-anon-user-menu-pages'
);
$additionalData = [
'data-anon-editor' => [
'data-link-learn-more' => $learnMoreLink->getTemplateData(),
'msgLearnMore' => $this->msg( 'vector-anon-user-menu-pages' )
]
if ( $isAnonEditorLinksEnabled ) {
$anonEditorLabelLinkData = [
'text' => $this->msg( 'vector-anon-user-menu-pages-learn' )->text(),
'href' => Title::newFromText( $this->msg( 'vector-intro-page' )->text() )->getLocalURL(),
'aria-label' => $this->msg( 'vector-anon-user-menu-pages-label' )->text(),
];
$anonEditorLabelLink = new SkinComponentLink(
'', $anonEditorLabelLinkData, $this->localizer, $this->linkOptions
);
$anonEditorLabelLinkHtml = $anonEditorLabelLink->getTemplateData()[ 'html' ];
$dropdownMenus[] = new VectorComponentMenu( [
'label' => $this->msg( 'vector-anon-user-menu-pages' )->text() . " " . $anonEditorLabelLinkHtml,
] + $portletData[ 'data-user-menu-anon-editor' ] );
}
} else {
if ( isset( $portletData[ 'data-user-menu-logout' ] ) ) {
$dropdownMenus[] = new VectorComponentMenu( [
'label' => null
] + $portletData[ 'data-user-menu-logout' ] );
}
}
return $additionalData + [
return $dropdownMenus;
}
/**
* @inheritDoc
*/
public function getTemplateData(): array {
$portletData = $this->portletData;
$user = $this->user;
$isDefaultAnonUserLinks = count( $portletData['data-user-menu']['array-items'] ) === 2;
$isAnonEditorLinksEnabled = isset( $portletData['data-user-menu-anon-editor']['is-empty'] )
&& !$portletData['data-user-menu-anon-editor']['is-empty'];
$overflowMenu = new VectorComponentMenu( [
'label' => null,
] + $portletData[ 'data-vector-user-menu-overflow' ] );
return [
'is-temp-user' => $user->isTemp(),
'is-wide' => count( $overflowMenu ) > 3,
'data-user-links-overflow-menu' => $overflowMenu->getTemplateData(),
'data-user-links-dropdown' => $userMenuDropdown->getTemplateData(),
'data-dropdown-menu' => $userMenu->getTemplateData(),
'data-account-links' => $this->accountMenu->getTemplateData(),
'data-user-links-dropdown' => $this->getDropdown( $isDefaultAnonUserLinks, $isAnonEditorLinksEnabled )
->getTemplateData(),
'data-user-links-dropdown-menus' => array_map( static function ( $menu ) {
return $menu->getTemplateData();
}, $this->getDropdownMenus( $isDefaultAnonUserLinks, $isAnonEditorLinksEnabled ) ),
];
}
}

View file

@ -258,12 +258,14 @@ class Hooks implements
* @internal used inside ::updateUserLinksItems
* @param SkinTemplate $sk
* @param array &$content_navigation
* @suppress PhanTypeInvalidDimOffset
*/
private static function updateUserLinksDropdownItems( $sk, &$content_navigation ) {
// For logged-in users in modern Vector, rearrange some links in the personal toolbar.
$user = $sk->getUser();
$isTemp = $user->isTemp();
$isRegistered = $user->isRegistered();
if ( $isTemp ) {
if ( isset( $content_navigation['user-page']['tmpuserpage'] ) ) {
$content_navigation['user-page']['tmpuserpage']['collapsible'] = true;
@ -283,28 +285,47 @@ class Hooks implements
if ( isset( $content_navigation['user-menu']['watchlist'] ) ) {
$content_navigation['user-menu']['watchlist']['collapsible'] = true;
}
// Remove logout link from user-menu and recreate it in SkinVector,
// Anon editor links handled manually in new anon editor menu
$logoutMenu = [];
if ( isset( $content_navigation['user-menu']['logout'] ) ) {
$logoutMenu['logout'] = $content_navigation['user-menu']['logout'];
$logoutMenu['logout']['id'] = 'pt-logout';
unset( $content_navigation['user-menu']['logout'] );
}
$content_navigation['user-menu-logout'] = $logoutMenu;
if ( $isRegistered ) {
// Prefix user link items with associated icon.
// Don't show icons for anon menu items (besides login and create account).
// Loop through each menu to check/append its link classes.
self::updateMenuItems( $content_navigation, 'user-menu' );
} else {
// Remove "Not logged in" from personal menu dropdown for anon users.
unset( $content_navigation['user-menu']['anonuserpage'] );
self::updateMenuItems( $content_navigation, 'user-menu-logout' );
}
if ( !$isRegistered || $isTemp ) {
// "Create account" link is handled manually by Vector
unset( $content_navigation['user-menu']['createaccount'] );
// "Login" link is handled manually by Vector
unset( $content_navigation['user-menu']['login'] );
// Remove "Not logged in" from personal menu dropdown for anon users.
unset( $content_navigation['user-menu']['anonuserpage'] );
// Remove duplicate "Login" link added by SkinTemplate::buildPersonalUrls if group read permissions
// are set to false.
unset( $content_navigation['user-menu']['login-private'] );
// Make login and create account collapsible
$content_navigation['user-menu']['login']['collapsible'] = true;
$content_navigation['user-menu']['createaccount']['collapsible'] = true;
// Anon editor links handled manually in new anon editor menu
$anonEditorMenu = [];
if ( isset( $content_navigation['user-menu']['anoncontribs'] ) ) {
$anonEditorMenu['anoncontribs'] = $content_navigation['user-menu']['anoncontribs'];
$anonEditorMenu['anoncontribs']['id'] = 'pt-anoncontribs';
unset( $content_navigation['user-menu']['anoncontribs'] );
}
if ( isset( $content_navigation['user-menu']['anontalk'] ) ) {
$anonEditorMenu['anontalk'] = $content_navigation['user-menu']['anontalk'];
$anonEditorMenu['anontalk']['id'] = 'pt-anontalk';
unset( $content_navigation['user-menu']['anontalk'] );
}
$content_navigation['user-menu-anon-editor'] = $anonEditorMenu;
// Only show icons for anon menu items (login and create account).
self::updateMenuItems( $content_navigation, 'user-menu' );
}
}

View file

@ -5,12 +5,9 @@ namespace MediaWiki\Skins\Vector;
use ExtensionRegistry;
use MediaWiki\MediaWikiServices;
use MediaWiki\Skins\Vector\Components\VectorComponentDropdown;
use MediaWiki\Skins\Vector\Components\VectorComponentIconLink;
use MediaWiki\Skins\Vector\Components\VectorComponentLanguageButton;
use MediaWiki\Skins\Vector\Components\VectorComponentLanguageDropdown;
use MediaWiki\Skins\Vector\Components\VectorComponentMainMenu;
use MediaWiki\Skins\Vector\Components\VectorComponentMenu;
use MediaWiki\Skins\Vector\Components\VectorComponentMenuListItem;
use MediaWiki\Skins\Vector\Components\VectorComponentMenuVariants;
use MediaWiki\Skins\Vector\Components\VectorComponentPageTools;
use MediaWiki\Skins\Vector\Components\VectorComponentPinnableContainer;
@ -308,11 +305,7 @@ class SkinVector22 extends SkinMustache {
$langButtonClass = $langData['class'] ?? '';
$ulsLabels = $this->getULSLabels();
$returnto = $this->getReturnToParam();
$user = $this->getUser();
$logoutData = $this->buildLogoutLinkData();
$loginLinkData = $this->buildLoginData( $returnto, $this->useCombinedLoginLink() );
$createAccountData = $this->buildCreateAccountData( $returnto );
$localizer = $this->getContext();
$tocData = $parentData['data-toc'];
@ -368,52 +361,8 @@ class SkinVector22 extends SkinMustache {
'data-vector-user-links' => new VectorComponentUserLinks(
$localizer,
$user,
new VectorComponentMenu(
$portlets['data-user-menu']
),
new VectorComponentMenu( [
// this menu should have no label
'label' => '',
] + $portlets[ 'data-vector-user-menu-overflow' ] ),
new VectorComponentMenu(
[],
$user->isRegistered() ? [
new VectorComponentMenuListItem(
new VectorComponentIconLink(
$logoutData[ 'href'],
$logoutData[ 'text' ],
$logoutData[ 'icon' ],
$this,
$logoutData[ 'single-id' ],
),
'vector-user-menu-logout',
'pt-logout'
)
] : [
new VectorComponentMenuListItem(
new VectorComponentIconLink(
$createAccountData['href'] ?? null,
$createAccountData['text'] ?? null,
$createAccountData['icon'] ?? null,
$this,
$createAccountData['single-id']
),
'vector-user-menu-create-account user-links-collapsible-item',
'pt-createaccount',
),
new VectorComponentMenuListItem(
new VectorComponentIconLink(
$loginLinkData['href'] ?? null,
$loginLinkData['text'] ?? null,
$loginLinkData['icon'] ?? null,
$this,
$loginLinkData['single-id']
),
'vector-user-menu-login user-links-collapsible-item',
'pt-login',
)
]
),
$portlets,
$this->getOptions()['link']
),
'data-lang-btn' => $langData ? new VectorComponentLanguageDropdown(
$ulsLabels['label'],

View file

@ -27,5 +27,5 @@
{{{html-vector-heading-icon}}}<span class="vector-menu-heading-label">{{label}}</span>
</label>
{{! FIXME: Rename this class to vector-dropdown-content. It currently clashes with MenuContents mustache template }}
<div class="vector-menu-content">
<div class="vector-menu-content vector-dropdown-content">

View file

@ -5,7 +5,7 @@
<div id="{{id}}" class="vector-menu{{#class}} {{.}}{{/class}}" {{{html-tooltip}}} {{{html-user-language-attributes}}}>
{{#label}}
<div class="vector-menu-heading{{#label-class}} {{.}}{{/label-class}}">
{{.}}
{{{.}}}
</div>
{{/label}}
{{>MenuContents}}

View file

@ -1,15 +1,4 @@
{{#data-user-links-dropdown}}{{>Dropdown/Open}}{{/data-user-links-dropdown}}
{{#is-anon}}
{{#is-temp-user}}
{{>UserLinks__templogin}}
{{/is-temp-user}}
{{^is-temp-user}}
{{>UserLinks__login}}
{{/is-temp-user}}
{{/is-anon}}
{{#data-dropdown-menu}}{{>MenuContents}}{{/data-dropdown-menu}}
{{^is-anon}}
{{!-- The #pt-logout ID is required for the AJAX enabled logout in mediawiki.page.ready to work.}}
{{#data-account-links}}{{>MenuContents}}{{/data-account-links}}
{{/is-anon}}
{{#is-anon}}{{#is-temp-user}}{{>UserLinks__templogin}}{{/is-temp-user}}{{/is-anon}}
{{#data-user-links-dropdown-menus}}{{>Menu}}{{/data-user-links-dropdown-menus}}
{{#data-user-links-dropdown}}{{>Dropdown/Close}}{{/data-user-links-dropdown}}

View file

@ -1,8 +0,0 @@
{{#data-account-links}}{{>MenuContents}}{{/data-account-links}}
{{#data-anon-editor}}
<div class="vector-user-menu-anon-editor">
<p>
{{{msgLearnMore}}} {{#data-link-learn-more}}{{>IconLink}}{{/data-link-learn-more}}
</p>
</div>
{{/data-anon-editor}}

View file

@ -20,6 +20,7 @@
* Dropdown container
*/
.vector-dropdown {
.vector-dropdown-content,
> .vector-menu-content {
background-color: @background-color-base;
border: @border-width-base @border-style-base @border-color-base;
@ -33,6 +34,7 @@
}
.vector-feature-page-tools-enabled & {
padding: 4px 0;
// TODO Add consistent min/max values for dropdowns in T316055
width: max-content;
max-width: 200px;
@ -40,6 +42,25 @@
}
}
.vector-feature-page-tools-enabled .vector-pinnable-element,
.vector-dropdown-content {
.vector-menu-heading {
.mixin-vector-dropdown-menu-item();
.mixin-vector-menu-heading();
}
.mw-list-item a {
// Mirror styles from Dropdown.less
.mixin-vector-dropdown-menu-item();
color: @color-link;
}
> *:not( :last-child ) {
// Apply bottom border to every children of dropdown contents and pinnable elements except the last
border-bottom: @border-width-base @border-style-base @colorGray14;
}
}
/**
* Dropdown menu items.
*/

View file

@ -1,29 +1,5 @@
@import '../../common/variables.less';
.vector-feature-page-tools-enabled .vector-pinnable-element {
& > *:not( :last-child ) {
// Apply bottom border to every children of pinnable elements except the last
border-bottom: @border-width-base @border-style-base @colorGray14;
}
.vector-menu-heading {
.mixin-vector-dropdown-menu-item();
.mixin-vector-menu-heading();
}
.mw-list-item a {
// Mirror styles from Dropdown.less
.mixin-vector-dropdown-menu-item();
color: @color-link;
}
}
// TODO: merge this into the selector above after page tools is enabled everywhere
.vector-unpinned-container .vector-pinnable-element {
padding: 4px 0;
}
// TODO: merge this into the selector above after page tools is enabled everywhere
.vector-pinned-container .vector-pinnable-element {
// Make the heading border line up with the edge of the text
// (Whereas in a dropdown, the border lines up with the edge of the dropdown container).

View file

@ -45,6 +45,9 @@
}
}
// Used to hide collapsible items inside the dropdown menu
// as well as the dropdown menu itself when the menu is empty
&.user-links-collapsible-item,
.user-links-collapsible-item {
@media ( min-width: @min-width-tablet ) {
display: none;
@ -52,6 +55,7 @@
}
}
// FIXME: Remove selector after Iac0586893fec26a8a6c2c904ce08fbf1e19b339c has been in prod for a week
.vector-user-menu-logout,
.vector-user-menu-create-account,
.vector-user-menu-login,
@ -117,8 +121,20 @@
/**
* Dropdown menu items- Special treatment for special links.
*/
#p-user-menu-anon-editor .vector-menu-heading {
display: block;
a:before {
content: '@{msg-parentheses-start}';
}
a:after {
content: '@{msg-parentheses-end}';
}
}
// Anon editor notice i.e. "Pages for logged out editors".
// FIXME: Remove selector after Iac0586893fec26a8a6c2c904ce08fbf1e19b339c has been in prod for a week
.vector-user-menu-anon-editor {
.mixin-vector-dropdown-menu-item();
@ -140,11 +156,13 @@
}
}
// FIXME: Remove selector after Iac0586893fec26a8a6c2c904ce08fbf1e19b339c has been in prod for a week
// Login link.
.vector-user-menu-login {
border-bottom: @border-width-base @border-style-base @colorGray14;
}
// FIXME: Remove selector after Iac0586893fec26a8a6c2c904ce08fbf1e19b339c has been in prod for a week
// Logout link
.vector-user-menu-logout {
border-top: @border-width-base @border-style-base @colorGray14;

View file

@ -16,7 +16,7 @@ exports[`Sticky header renders 1`] = `
<label id=\\"vector-sticky-header-toc-label\\" for=\\"vector-sticky-header-toc-checkbox\\" class=\\"vector-menu-heading mw-ui-button mw-ui-quiet mw-ui-icon mw-ui-icon-element mw-ui-icon-wikimedia-listBullet\\" tabindex=\\"-1\\">
<span class=\\"vector-menu-heading-label\\"></span>
</label>
<div class=\\"vector-menu-content\\">
<div class=\\"vector-menu-content vector-dropdown-content\\">
</div>
</div> <div class=\\"vector-sticky-header-context-bar-primary\\"></div>

View file

@ -18,33 +18,38 @@ exports[`UserLinks renders 1`] = `
</div>
</div>
<div id=\\"p-personal\\" class=\\"vector-menu vector-dropdown vector-menu-dropdown mw-portlet mw-portlet-personal vector-user-menu vector-user-menu-logged-in vector-menu-dropdown\\">
<div id=\\"p-personal\\" class=\\"vector-menu vector-dropdown vector-menu-dropdown vector-user-menu vector-user-menu-logged-in vector-menu-dropdown\\">
<input type=\\"checkbox\\" id=\\"p-personal-checkbox\\" role=\\"button\\" aria-haspopup=\\"true\\" data-event-name=\\"ui.dropdown-p-personal\\" class=\\"vector-menu-checkbox\\">
<label id=\\"p-personal-label\\" for=\\"p-personal-checkbox\\" class=\\"vector-menu-heading\\">
<span class=\\"vector-menu-heading-label\\">Personal tools</span>
</label>
<div class=\\"vector-menu-content\\">
<div class=\\"vector-menu-content vector-dropdown-content\\">
<div id=\\"\\" class=\\"vector-menu\\">
<div class=\\"vector-menu-content\\">
<ul class=\\"vector-menu-content-list\\">
<li id=\\"pt-userpage\\" class=\\"user-links-collapsible-item mw-list-item\\"><a class=\\"mw-ui-icon mw-ui-icon-before mw-ui-icon-userAvatar mw-ui-icon-wikimedia-userAvatar\\" href=\\"/wiki/User:Admin\\" title=\\"Your user page [.]\\" accesskey=\\".\\"><span>Admin</span></a></li>
<li id=\\"pt-mytalk\\" class=\\"mw-list-item\\"><a class=\\"mw-ui-icon mw-ui-icon-before mw-ui-icon-userTalk mw-ui-icon-wikimedia-userTalk\\" href=\\"/wiki/User_talk:Admin\\" title=\\"Your talk page [n]\\" accesskey=\\"n\\"><span>Talk</span></a></li>
<li id=\\"pt-sandbox\\" class=\\"new mw-list-item\\"><a class=\\"mw-ui-icon mw-ui-icon-before mw-ui-icon-sandbox mw-ui-icon-wikimedia-sandbox\\" href=\\"/w/index.php?title=User:Admin/sandbox&amp;action=edit&amp;redlink=1\\" title=\\"Your sandbox (page does not exist)\\"><span>Sandbox</span></a></li>
<li id=\\"pt-preferences\\" class=\\"mw-list-item\\"><a class=\\"mw-ui-icon mw-ui-icon-before mw-ui-icon-settings mw-ui-icon-wikimedia-settings\\" href=\\"/wiki/Special:Preferences\\" title=\\"Your preferences\\"><span>Preferences</span></a></li>
<li id=\\"pt-betafeatures\\" class=\\"mw-list-item\\"><a class=\\"mw-ui-icon mw-ui-icon-before mw-ui-icon-labFlask mw-ui-icon-wikimedia-labFlask\\" href=\\"/wiki/Special:Preferences#mw-prefsection-betafeatures\\" title=\\"Beta features\\"><span>Beta</span></a></li>
<li id=\\"pt-watchlist\\" class=\\"user-links-collapsible-item mw-list-item\\"><a class=\\"mw-ui-icon mw-ui-icon-before mw-ui-icon-watchlist mw-ui-icon-wikimedia-watchlist\\" href=\\"/wiki/Special:Watchlist\\" title=\\"A list of pages you are monitoring for changes [l]\\" accesskey=\\"l\\"><span>Watchlist</span></a></li>
<li id=\\"pt-uploads\\" class=\\"mw-list-item\\"><a class=\\"mw-ui-icon mw-ui-icon-before mw-ui-icon-imageGallery mw-ui-icon-wikimedia-imageGallery\\" href=\\"/w/index.php?title=Special:ListFiles/Admin&amp;ilshowall=1\\" title=\\"List of files you have uploaded\\"><span>Uploads</span></a></li>
<li id=\\"pt-mycontris\\" class=\\"mw-list-item\\"><a class=\\"mw-ui-icon mw-ui-icon-before mw-ui-icon-userContributions mw-ui-icon-wikimedia-userContributions\\" href=\\"/wiki/Special:Contributions/Admin\\" title=\\"A list of your contributions [y]\\" accesskey=\\"y\\"><span>Contributions</span></a></li>
<li id=\\"pt-userpage\\" class=\\"user-links-collapsible-item mw-list-item\\"><a href=\\"/wiki/User:Admin\\" title=\\"Your user page [.]\\" accesskey=\\".\\"><span class=\\"mw-ui-icon mw-ui-icon-userAvatar mw-ui-icon-wikimedia-userAvatar\\"></span> <span>Admin</span></a></li>
<li id=\\"pt-mytalk\\" class=\\"mw-list-item\\"><a href=\\"/wiki/User_talk:Admin\\" title=\\"Your talk page [n]\\" accesskey=\\"n\\"><span class=\\"mw-ui-icon mw-ui-icon-userTalk mw-ui-icon-wikimedia-userTalk\\"></span> <span>Talk</span></a></li>
<li id=\\"pt-sandbox\\" class=\\"new mw-list-item\\"><a href=\\"/w/index.php?title=User:Admin/sandbox&amp;action=edit&amp;redlink=1\\" title=\\"Your sandbox (page does not exist)\\"><span class=\\"mw-ui-icon mw-ui-icon-sandbox mw-ui-icon-wikimedia-sandbox\\"></span> <span>Sandbox</span></a></li>
<li id=\\"pt-preferences\\" class=\\"mw-list-item\\"><a href=\\"/wiki/Special:Preferences\\" title=\\"Your preferences\\"><span class=\\"mw-ui-icon mw-ui-icon-settings mw-ui-icon-wikimedia-settings\\"></span> <span>Preferences</span></a></li>
<li id=\\"pt-betafeatures\\" class=\\"mw-list-item\\"><a href=\\"/wiki/Special:Preferences#mw-prefsection-betafeatures\\" title=\\"Beta features\\"><span class=\\"mw-ui-icon mw-ui-icon-labFlask mw-ui-icon-wikimedia-labFlask\\"></span> <span>Beta</span></a></li>
<li id=\\"pt-watchlist\\" class=\\"user-links-collapsible-item mw-list-item\\"><a href=\\"/wiki/Special:Watchlist\\" title=\\"A list of pages you are monitoring for changes [l]\\" accesskey=\\"l\\"><span class=\\"mw-ui-icon mw-ui-icon-watchlist mw-ui-icon-wikimedia-watchlist\\"></span> <span>Watchlist</span></a></li>
<li id=\\"pt-uploads\\" class=\\"mw-list-item\\"><a href=\\"/w/index.php?title=Special:ListFiles/Admin&amp;ilshowall=1\\" title=\\"List of files you have uploaded\\"><span class=\\"mw-ui-icon mw-ui-icon-imageGallery mw-ui-icon-wikimedia-imageGallery\\"></span> <span>Uploads</span></a></li>
<li id=\\"pt-mycontris\\" class=\\"mw-list-item\\"><a href=\\"/wiki/Special:Contributions/Admin\\" title=\\"A list of your contributions [y]\\" accesskey=\\"y\\"><span class=\\"mw-ui-icon mw-ui-icon-userContributions mw-ui-icon-wikimedia-userContributions\\"></span> <span>Contributions</span></a></li>
<li id=\\"pt-custom\\" class=\\"mw-list-item mw-list-item-js\\">Gadget added item</li>
</ul>
</div>
</div>
<div id=\\"\\" class=\\"vector-menu\\">
<div class=\\"vector-menu-content\\">
<ul class=\\"vector-menu-content-list\\"><li class=\\"vector-user-menu-logout\\" id=\\"pt-logout\\"><a data-mw=\\"interface\\" href=\\"/logout\\"><span>Log out</span></a>
</li></ul>
<ul class=\\"vector-menu-content-list\\">
<li id=\\"ca-logout\\" class=\\"mw-list-item\\"><a data-mw=\\"interface\\" href=\\"/w/index.php?title=Special:UserLogout&amp;returnto=Main+Page\\" title=\\"Log out\\"><span class=\\"mw-ui-icon mw-ui-icon-logOut mw-ui-icon-wikimedia-logOut\\"></span> <span>Log out</span></a></li>
</ul>
</div>
</div>
</div>
</div></nav>

View file

@ -31,34 +31,26 @@ const templateData = {
},
'data-user-links-dropdown': {
id: 'p-personal',
class: 'mw-portlet mw-portlet-personal vector-user-menu vector-user-menu-logged-in vector-menu-dropdown',
class: 'vector-user-menu vector-user-menu-logged-in vector-menu-dropdown',
label: 'Personal tools'
},
'data-dropdown-menu': {
'data-user-links-dropdown-menus': [ {
'html-items': `
<li id="pt-userpage" class="user-links-collapsible-item mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-userAvatar mw-ui-icon-wikimedia-userAvatar" href="/wiki/User:Admin" title="Your user page [.]" accesskey="."><span>Admin</span></a></li>
<li id="pt-mytalk" class="mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-userTalk mw-ui-icon-wikimedia-userTalk" href="/wiki/User_talk:Admin" title="Your talk page [n]" accesskey="n"><span>Talk</span></a></li>
<li id="pt-sandbox" class="new mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-sandbox mw-ui-icon-wikimedia-sandbox" href="/w/index.php?title=User:Admin/sandbox&amp;action=edit&amp;redlink=1" title="Your sandbox (page does not exist)"><span>Sandbox</span></a></li>
<li id="pt-preferences" class="mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-settings mw-ui-icon-wikimedia-settings" href="/wiki/Special:Preferences" title="Your preferences"><span>Preferences</span></a></li>
<li id="pt-betafeatures" class="mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-labFlask mw-ui-icon-wikimedia-labFlask" href="/wiki/Special:Preferences#mw-prefsection-betafeatures" title="Beta features"><span>Beta</span></a></li>
<li id="pt-watchlist" class="user-links-collapsible-item mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-watchlist mw-ui-icon-wikimedia-watchlist" href="/wiki/Special:Watchlist" title="A list of pages you are monitoring for changes [l]" accesskey="l"><span>Watchlist</span></a></li>
<li id="pt-uploads" class="mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-imageGallery mw-ui-icon-wikimedia-imageGallery" href="/w/index.php?title=Special:ListFiles/Admin&amp;ilshowall=1" title="List of files you have uploaded"><span>Uploads</span></a></li>
<li id="pt-mycontris" class="mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-userContributions mw-ui-icon-wikimedia-userContributions" href="/wiki/Special:Contributions/Admin" title="A list of your contributions [y]" accesskey="y"><span>Contributions</span></a></li>
<li id="pt-userpage" class="user-links-collapsible-item mw-list-item"><a href="/wiki/User:Admin" title="Your user page [.]" accesskey="."><span class="mw-ui-icon mw-ui-icon-userAvatar mw-ui-icon-wikimedia-userAvatar"></span> <span>Admin</span></a></li>
<li id="pt-mytalk" class="mw-list-item"><a href="/wiki/User_talk:Admin" title="Your talk page [n]" accesskey="n"><span class="mw-ui-icon mw-ui-icon-userTalk mw-ui-icon-wikimedia-userTalk"></span> <span>Talk</span></a></li>
<li id="pt-sandbox" class="new mw-list-item"><a href="/w/index.php?title=User:Admin/sandbox&amp;action=edit&amp;redlink=1" title="Your sandbox (page does not exist)"><span class="mw-ui-icon mw-ui-icon-sandbox mw-ui-icon-wikimedia-sandbox"></span> <span>Sandbox</span></a></li>
<li id="pt-preferences" class="mw-list-item"><a href="/wiki/Special:Preferences" title="Your preferences"><span class="mw-ui-icon mw-ui-icon-settings mw-ui-icon-wikimedia-settings"></span> <span>Preferences</span></a></li>
<li id="pt-betafeatures" class="mw-list-item"><a href="/wiki/Special:Preferences#mw-prefsection-betafeatures" title="Beta features"><span class="mw-ui-icon mw-ui-icon-labFlask mw-ui-icon-wikimedia-labFlask"></span> <span>Beta</span></a></li>
<li id="pt-watchlist" class="user-links-collapsible-item mw-list-item"><a href="/wiki/Special:Watchlist" title="A list of pages you are monitoring for changes [l]" accesskey="l"><span class="mw-ui-icon mw-ui-icon-watchlist mw-ui-icon-wikimedia-watchlist"></span> <span>Watchlist</span></a></li>
<li id="pt-uploads" class="mw-list-item"><a href="/w/index.php?title=Special:ListFiles/Admin&amp;ilshowall=1" title="List of files you have uploaded"><span class="mw-ui-icon mw-ui-icon-imageGallery mw-ui-icon-wikimedia-imageGallery"></span> <span>Uploads</span></a></li>
<li id="pt-mycontris" class="mw-list-item"><a href="/wiki/Special:Contributions/Admin" title="A list of your contributions [y]" accesskey="y"><span class="mw-ui-icon mw-ui-icon-userContributions mw-ui-icon-wikimedia-userContributions"></span> <span>Contributions</span></a></li>
<li id="pt-custom" class="mw-list-item mw-list-item-js">Gadget added item</li>
`
},
'data-account-links': [
{
'data-items': [
{
'item-id': 'pt-logout',
'item-class': 'vector-user-menu-logout',
href: '/logout',
text: 'Log out'
}
]
}
]
}, {
'html-items': `
<li id="ca-logout" class="mw-list-item"><a data-mw="interface" href="/w/index.php?title=Special:UserLogout&amp;returnto=Main+Page" title="Log out"><span class="mw-ui-icon mw-ui-icon-logOut mw-ui-icon-wikimedia-logOut"></span> <span>Log out</span></a></li>
`
} ]
};
const dropdownPartials = {

View file

@ -508,12 +508,23 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
'createaccount' => [ 'class' => [], 'icon' => 'createaccount' ],
'login' => [ 'class' => [], 'icon' => 'login' ],
'login-private' => [ 'class' => [], 'icon' => 'login-private' ],
'anontalk' => [ 'class' => [], 'icon' => 'anontalk' ],
'anoncontribs' => [ 'class' => [], 'icon' => 'anoncontribs' ],
],
];
$updateUserLinksDropdownItems->invokeArgs( null, [ $skin, &$contentAnon ] );
$this->assertTrue(
count( $contentAnon['user-menu'] ) === 0,
'Anon user page, create account, login, and login private links are removed from anon user links dropdown'
count( $contentAnon['user-menu'] ) === 2 &&
isset( $contentAnon['user-menu']['createaccount'] ) &&
isset( $contentAnon['user-menu']['login'] ),
'Anon user page, login private and anon talk, anon contribs links are removed from user-menu'
);
$this->assertTrue(
count( $contentAnon['user-menu'] ) === 2 &&
isset( $contentAnon['user-menu-anon-editor']['anontalk'] ) &&
isset( $contentAnon['user-menu-anon-editor']['anoncontribs'] ),
'Anon talk, anon contribs links are moved to user-menu-anon-editor'
);
// Registered user
@ -549,6 +560,9 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
$this->assertFalse( isset( $contentRegistered['user-menu']['logout'] ),
'Logout link in user links dropdown is not set'
);
$this->assertTrue( isset( $contentRegistered['user-menu-logout']['logout'] ),
'Logout link in user links dropdown is not set'
);
}
/**