PageActions menu elements should be built by using IMenuElement

To simplify and make code reusable all menu elements should be built
by using Builder pattern. This a first step, first we the pageActions
methods should return Group object/IMenuElement objects, then in the
second step we will extract this logic into Director/Builder
classes leaving SkinMinerva class much smaller.

Bug: T221792
Change-Id: Ic51c4ca4139919fc3c5f3ada8b1b2fe5d23031d7
This commit is contained in:
Piotr Miazga 2019-05-21 19:01:00 +02:00
parent cb7dfc2dc6
commit 9d1217e13e
4 changed files with 199 additions and 71 deletions

View File

@ -34,7 +34,7 @@ class Group {
/** /**
* Return entries count * Return entries count
* *
* @return int * @return bool
*/ */
public function hasEntries() { public function hasEntries() {
return count( $this->entries ) > 0; return count( $this->entries ) > 0;

View File

@ -0,0 +1,111 @@
<?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\Minerva\Menu\PageActions;
use Message;
use MediaWiki\Minerva\Menu\IMenuEntry;
class PageActionMenuEntry implements IMenuEntry {
/**
* Menu entry unique identifier
* @var string
*/
private $name;
/**
* A definition of a menu component
* An array containing HTML attributes and label for the menu entry
* @var array
*/
private $component;
/**
* PageActionMenuEntry constructor.
* @param string $name Unique identifier
* @param string $href And URL menu entry points to
* @param string $componentClass A CSS class injected to component
* @param Message $message Message
*/
public function __construct( $name, $href, $componentClass, Message $message ) {
$this->name = $name;
$this->component = [
'href' => $href,
'class' => $componentClass,
'text' => $message->escaped()
];
}
/**
* Named constructor for easier class creation when we want to additionally set things
* like title or nodeId. Mostly a syntax sugar.
* @param string $name Unique identifier
* @param string $href And URL menu entry points to
* @param string $componentClass A CSS class injected to component
* @param Message $message Message
* @return self
*/
public static function create( $name, $href, $componentClass, Message $message ) {
return new PageActionMenuEntry( $name, $href, $componentClass, $message );
}
/**
* @inheritDoc
*/
public function getName() {
return $this->name;
}
/**
* @inheritDoc
*
*/
public function getCSSClasses(): array {
return [];
}
/**
* @inheritDoc
*/
public function getComponents(): array {
return [ $this->component ];
}
/**
* Set the menu entry title
* @param Message $message Title message
* @return $this
*/
public function setTitle( \Message $message ) {
$this->component['title'] = $message->escaped();
return $this;
}
/**
* Set the Menu entry ID html attribute
* @param string $nodeID
* @return $this
*/
public function setNodeID( $nodeID ) {
$this->component['id'] = $nodeID;
return $this;
}
}

View File

@ -20,6 +20,8 @@
use MediaWiki\MediaWikiServices; use MediaWiki\MediaWikiServices;
use MediaWiki\Minerva\Menu\Main\Director as MainMenuDirector; use MediaWiki\Minerva\Menu\Main\Director as MainMenuDirector;
use MediaWiki\Minerva\Menu\Group;
use MediaWiki\Minerva\Menu\PageActions\PageActionMenuEntry;
use MediaWiki\Minerva\SkinOptions; use MediaWiki\Minerva\SkinOptions;
use MediaWiki\Minerva\SkinUserPageHelper; use MediaWiki\Minerva\SkinUserPageHelper;
@ -838,23 +840,23 @@ class SkinMinerva extends SkinTemplate {
* @param BaseTemplate $tpl * @param BaseTemplate $tpl
*/ */
protected function preparePageActions( BaseTemplate $tpl ) { protected function preparePageActions( BaseTemplate $tpl ) {
$toolbar = []; $toolbar = new Group();
$overflowMenu = null; $overflowMenu = null;
if ( $this->isAllowedPageAction( 'switch-language' ) ) { if ( $this->isAllowedPageAction( 'switch-language' ) ) {
$toolbar[] = $this->createSwitchLanguageAction(); $toolbar->insertEntry( $this->createSwitchLanguageAction() );
} }
if ( $this->isAllowedPageAction( 'watch' ) ) { if ( $this->isAllowedPageAction( 'watch' ) ) {
$toolbar[] = $this->createWatchPageAction(); $toolbar->insertEntry( $this->createWatchPageAction() );
} }
if ( $this->isAllowedPageAction( 'history' ) ) { if ( $this->isAllowedPageAction( 'history' ) ) {
$toolbar[] = $this->getHistoryPageAction(); $toolbar->insertEntry( $this->getHistoryPageAction() );
} }
if ( $this->isAllowedPageAction( 'edit' ) ) { if ( $this->isAllowedPageAction( 'edit' ) ) {
$toolbar[] = $this->createEditPageAction(); $toolbar->insertEntry( $this->createEditPageAction() );
} }
if ( $this->isAllowedPageAction( SkinOptions::OPTION_OVERFLOW_SUBMENU ) ) { if ( $this->isAllowedPageAction( SkinOptions::OPTION_OVERFLOW_SUBMENU ) ) {
@ -862,7 +864,7 @@ class SkinMinerva extends SkinTemplate {
} }
$tpl->set( 'page_actions', [ $tpl->set( 'page_actions', [
'toolbar' => $toolbar, 'toolbar' => $toolbar->getEntries(),
'overflowMenu' => $overflowMenu 'overflowMenu' => $overflowMenu
] ); ] );
} }
@ -871,8 +873,7 @@ class SkinMinerva extends SkinTemplate {
* Creates the "edit" page action: the well-known pencil icon that, when tapped, will open an * Creates the "edit" page action: the well-known pencil icon that, when tapped, will open an
* editor with the lead section loaded. * editor with the lead section loaded.
* *
* @return array A map of HTML attributes and a "text" property to be used with the * @return PageActionMenuEntry An edit page actions menu entry
* pageActionMenu.mustache template.
*/ */
protected function createEditPageAction() { protected function createEditPageAction() {
$title = $this->getTitle(); $title = $this->getTitle();
@ -888,15 +889,15 @@ class SkinMinerva extends SkinTemplate {
$userBlockInfo = $user->getId() == 0 ? false : $user->isBlockedFrom( $title, true ); $userBlockInfo = $user->getId() == 0 ? false : $user->isBlockedFrom( $title, true );
$userCanEdit = $userQuickEditCheck && !$userBlockInfo; $userCanEdit = $userQuickEditCheck && !$userBlockInfo;
return [ return PageActionMenuEntry::create(
'item-id' => 'page-actions-edit', 'page-actions-edit',
'id' => 'ca-edit', $title->getLocalURL( $editArgs ),
'href' => $title->getLocalURL( $editArgs ), 'edit-page '
'class' => 'edit-page ' . MinervaUI::iconClass( $userCanEdit ? 'edit-enabled' : 'edit', 'element' ),
. MinervaUI::iconClass( $userCanEdit ? 'edit-enabled' : 'edit', 'element' ), $this->msg( 'mobile-frontend-editor-edit' )
'title' => $this->msg( 'mobile-frontend-pageaction-edit-tooltip' ), )
'text' => $this->msg( 'mobile-frontend-editor-edit' ) ->setTitle( $this->msg( 'mobile-frontend-pageaction-edit-tooltip' ) )
]; ->setNodeID( 'ca-edit' );
} }
/** /**
@ -904,8 +905,7 @@ class SkinMinerva extends SkinTemplate {
* add the page to or remove the page from the user's watchlist; or, if the user is logged out, * add the page to or remove the page from the user's watchlist; or, if the user is logged out,
* will direct the user's UA to Special:Login. * will direct the user's UA to Special:Login.
* *
* @return array A map of HTML attributes and a "text" property to be used with the * @return PageActionMenuEntry An watch/unwatch page actions menu entry
* pageActionMenu.mustache template.
*/ */
protected function createWatchPageAction() { protected function createWatchPageAction() {
$title = $this->getTitle(); $title = $this->getTitle();
@ -925,24 +925,24 @@ class SkinMinerva extends SkinTemplate {
$msg = $this->msg( 'watchthispage' ); $msg = $this->msg( 'watchthispage' );
$icon = 'watch'; $icon = 'watch';
} }
return [ $iconClass = MinervaUI::iconClass( $icon, 'element', 'watch-this-article' );
'item-id' => 'page-actions-watch',
'id' => 'ca-watch', return PageActionMenuEntry::create(
// Use blank icon to reserve space for watchstar icon once JS loads 'page-actions-watch',
'class' => MinervaUI::iconClass( $icon, 'element', 'watch-this-article' ) $href,
. $additionalClassNames, $iconClass . $additionalClassNames,
'title' => $msg, $msg
'text' => $msg, )
'href' => $href ->setTitle( $msg )
]; ->setNodeID( 'ca-watch' );
} }
/** /**
* Creates the "switch-language" action: the icon that, when tapped, opens the language * Creates the "switch-language" action: the icon that, when tapped, opens the language
* switcher. * switcher.
* *
* @return array A map of HTML attributes and a 'text' property to be used with the * @return PageActionMenuEntry A menu entry object that represents a map of HTML attributes
* pageActionMenu.mustache template. * and a 'text' property to be used with the pageActionMenu.mustache template.
*/ */
protected function createSwitchLanguageAction() { protected function createSwitchLanguageAction() {
$languageSwitcherLink = false; $languageSwitcherLink = false;
@ -956,13 +956,12 @@ class SkinMinerva extends SkinTemplate {
} else { } else {
$languageSwitcherClasses .= ' disabled'; $languageSwitcherClasses .= ' disabled';
} }
return [ return PageActionMenuEntry::create(
'item-id' => 'page-actions-languages', 'page-actions-languages',
'class' => MinervaUI::iconClass( 'language-switcher', 'element', $languageSwitcherClasses ), $languageSwitcherLink,
'href' => $languageSwitcherLink, MinervaUI::iconClass( 'language-switcher', 'element', $languageSwitcherClasses ),
'title' => $this->msg( 'mobile-frontend-language-article-heading' ), $this->msg( 'mobile-frontend-language-article-heading' )
'text' => $this->msg( 'mobile-frontend-language-article-heading' ) )->setTitle( $this->msg( 'mobile-frontend-language-article-heading' ) );
];
} }
/** /**
@ -976,16 +975,16 @@ class SkinMinerva extends SkinTemplate {
/** /**
* Creates a history action: An icon that links to the mobile history page. * Creates a history action: An icon that links to the mobile history page.
* *
* @return array A map of HTML attributes and a 'text' property to be used with the * @return PageActionMenuEntry A menu entry object that represents a map of HTML attributes
* pageActionMenu.mustache template. * and a 'text' property to be used with the pageActionMenu.mustache template.
*/ */
protected function getHistoryPageAction() { protected function getHistoryPageAction() {
return [ return new PageActionMenuEntry(
'item-id' => 'page-actions-history', 'page-actions-history',
'class' => MinervaUI::iconClass( 'clock' ), $this->getHistoryUrl( $this->getTitle() ),
'text' => $this->msg( 'mobile-frontend-history' ), MinervaUI::iconClass( 'clock' ),
'href' => $this->getHistoryUrl( $this->getTitle() ) $this->msg( 'mobile-frontend-history' )
]; );
} }
/** /**
@ -1006,20 +1005,22 @@ class SkinMinerva extends SkinTemplate {
$pageActions = $this->getUserPageHelper()->isUserPage() $pageActions = $this->getUserPageHelper()->isUserPage()
? $this->getUserNamespaceOverflowPageActions( $tpl ) ? $this->getUserNamespaceOverflowPageActions( $tpl )
: $this->getDefaultOverflowPageActions( $tpl ); : $this->getDefaultOverflowPageActions( $tpl );
return empty( $pageActions ) ? null : [ return $pageActions->hasEntries() ? [
'item-id' => 'page-actions-overflow', 'item-id' => 'page-actions-overflow',
'class' => MinervaUI::iconClass( 'page-actions-overflow' ), 'class' => MinervaUI::iconClass( 'page-actions-overflow' ),
'text' => $this->msg( 'minerva-page-actions-overflow' ), 'text' => $this->msg( 'minerva-page-actions-overflow' ),
'pageActions' => $pageActions 'pageActions' => $pageActions->getEntries()
]; ] : null;
} }
/** /**
* @param BaseTemplate $tpl * @param BaseTemplate $tpl
* @return array * @return Group
*/ */
private function getDefaultOverflowPageActions( BaseTemplate $tpl ) { private function getDefaultOverflowPageActions( BaseTemplate $tpl ) {
return array_values( array_filter( [ $group = new Group();
foreach ( [
$this->newOverflowPageAction( 'info', 'info', $tpl->data['nav_urls']['info']['href'] ?? null ), $this->newOverflowPageAction( 'info', 'info', $tpl->data['nav_urls']['info']['href'] ?? null ),
$this->newOverflowPageAction( $this->newOverflowPageAction(
'permalink', 'link', $tpl->data['nav_urls']['permalink']['href'] ?? null 'permalink', 'link', $tpl->data['nav_urls']['permalink']['href'] ?? null
@ -1030,7 +1031,12 @@ class SkinMinerva extends SkinTemplate {
$this->newOverflowPageAction( $this->newOverflowPageAction(
'cite', 'quotes', $tpl->data['nav_urls']['citethispage']['href'] ?? null 'cite', 'quotes', $tpl->data['nav_urls']['citethispage']['href'] ?? null
) )
] ) ); ] as $menuEntry ) {
if ( $menuEntry !== null ) {
$group->insertEntry( $menuEntry );
}
}
return $group;
} }
/** /**
@ -1049,11 +1055,12 @@ class SkinMinerva extends SkinTemplate {
/** /**
* @param BaseTemplate $tpl * @param BaseTemplate $tpl
* @return array * @return Group
*/ */
private function getUserNamespaceOverflowPageActions( BaseTemplate $tpl ) { private function getUserNamespaceOverflowPageActions( BaseTemplate $tpl ) {
$pageUser = $this->getUserPageHelper()->getPageUser(); $pageUser = $this->getUserPageHelper()->getPageUser();
return [ $group = new Group();
foreach ( [
$this->newOverflowPageAction( $this->newOverflowPageAction(
'uploads', 'upload', SpecialPage::getTitleFor( 'Uploads', $pageUser )->getLocalURL() 'uploads', 'upload', SpecialPage::getTitleFor( 'Uploads', $pageUser )->getLocalURL()
), ),
@ -1070,22 +1077,28 @@ class SkinMinerva extends SkinTemplate {
$this->newOverflowPageAction( $this->newOverflowPageAction(
'backlinks', 'articleRedirect', $tpl->data['nav_urls']['whatlinkshere']['href'] ?? null 'backlinks', 'articleRedirect', $tpl->data['nav_urls']['whatlinkshere']['href'] ?? null
) )
]; ] as $menuEntry ) {
if ( $menuEntry !== null ) {
$group->insertEntry( $menuEntry );
}
}
return $group;
} }
/** /**
* @param string $name * @param string $name
* @param string $icon Wikimedia UI icon name. * @param string $icon Wikimedia UI icon name.
* @param string|null $href * @param string|null $href
* @return array * @return PageActionMenuEntry|null
*/ */
private function newOverflowPageAction( $name, $icon, $href ) { private function newOverflowPageAction( $name, $icon, $href ) {
return $href ? [ return $href ?
'item-id' => 'page-actions-overflow-' . $name, new PageActionMenuEntry(
'class' => MinervaUI::iconClass( '', 'before', 'wikimedia-ui-' . $icon . '-base20' ), 'page-actions-overflow-' . $name,
'text' => $this->msg( 'minerva-page-actions-' . $name ), $href,
'href' => $href MinervaUI::iconClass( '', 'before', 'wikimedia-ui-' . $icon . '-base20' ),
] : null; $this->msg( 'minerva-page-actions-' . $name )
) : null;
} }
/** /**

View File

@ -1,10 +1,12 @@
<nav class="page-actions-menu"> <nav class="page-actions-menu">
<ul id="page-actions" class="page-actions-menu__list"> <ul id="page-actions" class="page-actions-menu__list">
{{#toolbar}} {{#toolbar}}
<li id="{{item-id}}" class="page-actions-menu__list-item"> <li id="{{name}}" class="page-actions-menu__list-item">
<a id="{{id}}" href="{{href}}" class="{{class}}" role="button" title="{{title}}"> {{#components}}
{{text}} <a id="{{id}}" href="{{href}}" class="{{class}}" data-event-name="{{data-event-name}}" role="button" title="{{title}}">
</a> {{text}}
</a>
{{/components}}
</li> </li>
{{/toolbar}} {{/toolbar}}
{{#overflowMenu}} {{#overflowMenu}}
@ -15,10 +17,12 @@
</label> </label>
<ul class="toolbar-overflow-menu__list"> <ul class="toolbar-overflow-menu__list">
{{#pageActions}} {{#pageActions}}
<li> <li id="{{name}}">
<a id="{{id}}" href="{{href}}" class="toolbar-overflow-menu__list-item {{class}}" title="{{title}}"> {{#components}}
{{text}} <a id="{{id}}" href="{{href}}" class="toolbar-overflow-menu__list-item {{class}}" title="{{title}}">
</a> {{text}}
</a>
{{/components}}
</li> </li>
{{/pageActions}} {{/pageActions}}
</ul> </ul>