diff --git a/i18n/en.json b/i18n/en.json
index 50bb3a8..884c89e 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -47,6 +47,7 @@
"mobile-frontend-user-page-member-since": "{{GENDER:$2|Joined}} $1",
"mobile-frontend-user-page-talk": "Talk",
"mobile-frontend-user-page-uploads": "Uploads",
+ "minerva-user-menu-button": "User menu",
"minerva-page-actions-overflow": "Secondary page actions submenu",
"minerva-page-actions-info": "Page information",
"minerva-page-actions-permalink": "Permanent link",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 7d24053..012ced5 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -56,6 +56,7 @@
"mobile-frontend-user-page-member-since": "Message below the heading. $1 is the user registration date. $2 is the gender associated with the user account.",
"mobile-frontend-user-page-talk": "Text of the link to the user's talk page\n{{Identical|Talk}}",
"mobile-frontend-user-page-uploads": "Text of the link to the user's uploads page\n{{Identical|Upload}}",
+ "minerva-user-menu-button": "Text describing the user menu",
"minerva-page-actions-overflow": "Text describing the secondary page menu button's action",
"minerva-page-actions-info": "In the secondary page menu, the page information button label",
"minerva-page-actions-permalink": "In the secondary page menu, the permanent link button label",
diff --git a/includes/menu/Definitions.php b/includes/menu/Definitions.php
index 8ccc1c0..2977931 100644
--- a/includes/menu/Definitions.php
+++ b/includes/menu/Definitions.php
@@ -24,6 +24,8 @@ use IContextSource;
use MediaWiki\Special\SpecialPageFactory;
use MediaWiki\Minerva\Menu\Entries\AuthMenuEntry;
use MediaWiki\Minerva\Menu\Entries\HomeMenuEntry;
+use MediaWiki\Minerva\Menu\Entries\LogInMenuEntry;
+use MediaWiki\Minerva\Menu\Entries\LogOutMenuEntry;
use MediaWiki\Minerva\Menu\Entries\SingleMenuEntry;
use Message;
use MinervaUI;
@@ -108,6 +110,32 @@ final class Definitions {
) );
}
+ /**
+ * Creates a log in or log out button.
+ *
+ * @param Group $group
+ * @throws MWException
+ */
+ public function insertLogInMenuItem( Group $group ) {
+ $group->insertEntry( new LogInMenuEntry(
+ $this->context,
+ $this->newLogInOutQuery( $this->newReturnToQuery() )
+ ) );
+ }
+
+ /**
+ * Creates a log in or log out button.
+ *
+ * @param Group $group
+ * @throws MWException
+ */
+ public function insertLogOutMenuItem( Group $group ) {
+ $group->insertEntry( new LogOutMenuEntry(
+ $this->context,
+ $this->newLogInOutQuery( $this->newReturnToQuery() )
+ ) );
+ }
+
/**
* Creates a login or logout button with a profile button.
*
diff --git a/includes/menu/Main/AdvancedBuilder.php b/includes/menu/Main/AdvancedBuilder.php
index e455910..2bd0539 100644
--- a/includes/menu/Main/AdvancedBuilder.php
+++ b/includes/menu/Main/AdvancedBuilder.php
@@ -97,13 +97,6 @@ final class AdvancedBuilder implements IBuilder {
private function getPersonalTools(): Group {
$group = new Group();
- $this->definitions->insertAuthMenuItem( $group );
-
- if ( $this->user->isLoggedIn() ) {
- $this->definitions->insertWatchlistMenuItem( $group );
- $this->definitions->insertContributionsMenuItem( $group );
- }
-
// Allow other extensions to add or override tools
Hooks::run( 'MobileMenu', [ 'personal', &$group ] );
return $group;
diff --git a/includes/menu/PageActions/UserNamespaceOverflowBuilder.php b/includes/menu/PageActions/UserNamespaceOverflowBuilder.php
index 286c80d..4edd291 100644
--- a/includes/menu/PageActions/UserNamespaceOverflowBuilder.php
+++ b/includes/menu/PageActions/UserNamespaceOverflowBuilder.php
@@ -121,7 +121,7 @@ class UserNamespaceOverflowBuilder implements IOverflowBuilder {
* Build entry based on the $toolbox element
*
* @param string $name
- * @param string $icon Wikimedia UI icon name.
+ * @param string $icon Icon CSS class name.
* @param string $toolboxIdx
* @param array $toolbox An array of common toolbox items from the sidebar menu
* @return PageActionMenuEntry|null
diff --git a/includes/menu/User/AdvancedUserMenuBuilder.php b/includes/menu/User/AdvancedUserMenuBuilder.php
new file mode 100644
index 0000000..4b985be
--- /dev/null
+++ b/includes/menu/User/AdvancedUserMenuBuilder.php
@@ -0,0 +1,100 @@
+context = $context;
+ $this->user = $user;
+ $this->definitions = $definitions;
+ $this->sandbox = $sandbox;
+ }
+
+ /**
+ * @inheritDoc
+ * @return Group
+ */
+ public function getGroup(): Group {
+ $group = new Group();
+ $group->insertEntry( new ProfileMenuEntry( $this->user ) );
+ $group->insertEntry( new SingleMenuEntry(
+ 'userTalk',
+ $this->context->msg( 'mobile-frontend-user-page-talk' )->escaped(),
+ $this->user->getUserPage()->getTalkPage()->getLocalURL(),
+ true,
+ null,
+ 'before',
+ 'wikimedia-ui-userTalk-base20'
+ ) );
+ if ( $this->sandbox ) {
+ $group->insertEntry( new SingleMenuEntry(
+ 'userSandbox',
+ $this->sandbox['text'],
+ $this->sandbox['href']
+ ) );
+ }
+ $this->definitions->insertWatchlistMenuItem( $group );
+ $this->definitions->insertContributionsMenuItem( $group );
+ if ( $this->user->isAnon() ) {
+ $this->definitions->insertLogInMenuItem( $group );
+ } else {
+ $this->definitions->insertLogOutMenuItem( $group );
+ }
+ return $group;
+ }
+}
diff --git a/includes/menu/User/DefaultUserMenuBuilder.php b/includes/menu/User/DefaultUserMenuBuilder.php
new file mode 100644
index 0000000..afe687a
--- /dev/null
+++ b/includes/menu/User/DefaultUserMenuBuilder.php
@@ -0,0 +1,35 @@
+builder = $builder;
+ $this->localizer = $localizer;
+ }
+
+ /**
+ * Build the menu data array that can be passed to views/javascript
+ * @return string|null
+ */
+ public function renderMenuData() {
+ $entries = $this->builder->getGroup()->getEntries();
+
+ foreach ( $entries as &$entry ) {
+ foreach ( $entry['components'] as &$component ) {
+ $component['class'] .= ' toggle-list-item__anchor--menu';
+ }
+ }
+
+ $templateParser = new TemplateParser( __DIR__ . '/../../../components' );
+ return empty( $entries )
+ ? null
+ : $templateParser->processTemplate( 'ToggleList', [
+ 'class' => 'minerva-user-menu',
+ 'checkboxID' => 'minerva-user-menu-checkbox',
+ 'toggleID' => 'minerva-user-menu-toggle', // See minerva.mustache too.
+ 'toggleClass' => MinervaUI::iconClass(
+ 'page-actions-overflow', 'element', 'wikimedia-ui-' . 'userAvatar' . '-base20'
+ ),
+ 'listClass' => 'minerva-user-menu-list toggle-list__list--drop-down', // See ToggleList/*.less.
+ 'text' => $this->localizer->msg( 'minerva-user-menu-button' )->escaped(),
+ 'items' => $entries
+ ] );
+ }
+}
diff --git a/includes/skins/MinervaTemplate.php b/includes/skins/MinervaTemplate.php
index 8d6315e..f6e6090 100644
--- a/includes/skins/MinervaTemplate.php
+++ b/includes/skins/MinervaTemplate.php
@@ -266,6 +266,7 @@ class MinervaTemplate extends BaseTemplate {
'postheadinghtml' => $data['postheadinghtml'] ?? '',
'haspageactions' => $hasPageActions,
'pageactionshtml' => $hasPageActions ? $this->getPageActionsHtml() : '',
+ 'userMenuHTML' => $data['userMenuHTML'],
'subtitle' => $data['subtitle'],
'contenthtml' => $this->getContentHtml( $data ),
'secondaryactionshtml' => $this->getSecondaryActionsHtml(),
diff --git a/includes/skins/SkinMinerva.php b/includes/skins/SkinMinerva.php
index 22e931f..45afae0 100644
--- a/includes/skins/SkinMinerva.php
+++ b/includes/skins/SkinMinerva.php
@@ -23,6 +23,10 @@ use MediaWiki\Minerva\Menu\Main\Director as MainMenuDirector;
use MediaWiki\Minerva\Permissions\IMinervaPagePermissions;
use MediaWiki\Minerva\SkinOptions;
use MediaWiki\Minerva\SkinUserPageHelper;
+use MediaWiki\Minerva\Menu\User\UserMenuDirector;
+use MediaWiki\Minerva\Menu\User\AdvancedUserMenuBuilder;
+use MediaWiki\Minerva\Menu\User\DefaultUserMenuBuilder;
+use MediaWiki\Minerva\Menu\Definitions;
/**
* Minerva: Born from the godhead of Jupiter with weapons!
@@ -89,6 +93,29 @@ class SkinMinerva extends SkinTemplate {
return $this->mainMenu;
}
+ /**
+ * @param BaseTemplate $tpl
+ * @return string|null
+ */
+ private function getUserMenuHTML( BaseTemplate $tpl ) {
+ $services = MediaWikiServices::getInstance();
+ $options = $services->getService( 'Minerva.SkinOptions' );
+ $context = RequestContext::getMain();
+ $definitions = new Definitions( $context, $services->getSpecialPageFactory() );
+ $director = new UserMenuDirector(
+ $options->get( SkinOptions::OPTION_AMC )
+ ? new AdvancedUserMenuBuilder(
+ $context,
+ $this->getUser(),
+ $definitions,
+ $tpl->getPersonalTools()['sandbox']['links'][0] ?? null
+ )
+ : new DefaultUserMenuBuilder(),
+ $this
+ );
+ return $director->renderMenuData();
+ }
+
/**
* Returns the site name for the footer, either as a text or tag
* @return string
@@ -156,6 +183,9 @@ class SkinMinerva extends SkinTemplate {
// Set the links for the main menu
$tpl->set( 'mainMenu', $this->getMainMenu()->getMenuData() );
+ // Set the links for page secondary actions
+ $tpl->set( 'userMenuHTML', $this->getUserMenuHTML( $tpl ) );
+
// Set the links for page secondary actions
$tpl->set( 'secondary_actions', $this->getSecondaryActions( $tpl ) );
@@ -774,14 +804,6 @@ class SkinMinerva extends SkinTemplate {
return [];
}
- /**
- * Minerva skin do use any of those, there is no need to calculate that
- * @return array
- */
- protected function buildPersonalUrls() {
- return [];
- }
-
/**
* Returns array of config variables that should be added only to this skin
* for use in JavaScript.
diff --git a/includes/skins/minerva.mustache b/includes/skins/minerva.mustache
index 5eff74b..655ad24 100644
--- a/includes/skins/minerva.mustache
+++ b/includes/skins/minerva.mustache
@@ -18,8 +18,15 @@
autocomplete="off" placeholder="{{placeholder}}" aria-label="{{placeholder}}"
value="{{search}}">
-