mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/MinervaNeue
synced 2024-12-01 09:16:34 +00:00
edb4385345
A list that toggles visibility via the checkbox hack is needed in at least two spots: the page actions overflow menu and the user menu. This patch makes several refactors to turn what was previously hardcoded into page actions a reusable component: - Start a new components directory. Components are reusable and composable. The subdirectories are organized by function, not ResourceLoader module bundling which greatly improves the ability to see a component's full functionality in one directory instead of examining the entire codebase. See updates to README. - Extract pageactions.less into: - ToggleList.less: LESS for any checkbox hack list. - DropDownList.less: LESS for lists that open downwards. - MenuListItem.less: LESS for list items of menus. The division makes it easier to see concerns, dependencies, and change code. - Move pageActionMenu.mustache to a component and extract ToggleList template. - Extract ToggleList.js from Toolbar.js. Bug: T214540 Change-Id: I171831469a6733c458bc5c7ba249a5096ca975b8
307 lines
9.9 KiB
PHP
307 lines
9.9 KiB
PHP
<?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
|
|
*/
|
|
use MediaWiki\MediaWikiServices;
|
|
use MediaWiki\Minerva\SkinOptions;
|
|
|
|
/**
|
|
* Extended Template class of BaseTemplate for mobile devices
|
|
*/
|
|
class MinervaTemplate extends BaseTemplate {
|
|
/** @var bool Specify whether the page is a special page */
|
|
protected $isSpecialPage;
|
|
|
|
/** @var bool Whether or not the user is on the Special:MobileMenu page */
|
|
protected $isSpecialMobileMenuPage;
|
|
|
|
/** @var bool Specify whether the page is main page */
|
|
protected $isMainPage;
|
|
|
|
/**
|
|
* Start render the page in template
|
|
*/
|
|
public function execute() {
|
|
$title = $this->getSkin()->getTitle();
|
|
$this->isSpecialPage = $title->isSpecialPage();
|
|
$this->isSpecialMobileMenuPage = $this->isSpecialPage &&
|
|
$title->isSpecial( 'MobileMenu' );
|
|
$this->isMainPage = $title->isMainPage();
|
|
$this->isMainPageTalk = $title->getSubjectPage()->isMainPage();
|
|
Hooks::run( 'MinervaPreRender', [ $this ] );
|
|
$this->render( $this->data );
|
|
}
|
|
|
|
/**
|
|
* Returns available page actions
|
|
* @return array
|
|
*/
|
|
protected function getPageActions() {
|
|
return $this->isFallbackEditor() ? [] : $this->data['page_actions'];
|
|
}
|
|
|
|
/**
|
|
* Returns template data for footer
|
|
*
|
|
* @param array $data Data used to build the page
|
|
* @return array
|
|
*/
|
|
protected function getFooterTemplateData( $data ) {
|
|
$groups = [];
|
|
|
|
foreach ( $data['footerlinks'] as $category => $links ) {
|
|
$items = [];
|
|
foreach ( $links as $link ) {
|
|
if ( isset( $this->data[$link] ) && $data[$link] !== '' && $data[$link] !== false ) {
|
|
$items[] = [
|
|
'category' => $category,
|
|
'name' => $link,
|
|
'linkhtml' => $data[$link],
|
|
];
|
|
}
|
|
}
|
|
$groups[] = [
|
|
'name' => $category,
|
|
'items' => $items,
|
|
];
|
|
}
|
|
|
|
// This turns off the footer id and allows us to distinguish the old footer with the new design
|
|
return [
|
|
'lastmodified' => $this->getHistoryLinkHtml( $data ),
|
|
'headinghtml' => $data['footer-site-heading-html'],
|
|
// Note mobile-license is only available on the mobile skin. It is outputted as part of
|
|
// footer-info on desktop hence the conditional check.
|
|
'licensehtml' => isset( $data['mobile-license'] ) ? $data['mobile-license'] : '',
|
|
'lists' => $groups,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get the HTML for rendering the available page actions
|
|
* @return string
|
|
*/
|
|
protected function getPageActionsHtml() {
|
|
$templateParser = new TemplateParser( __DIR__ . '/../../components' );
|
|
$pageActions = $this->getPageActions();
|
|
$html = '';
|
|
|
|
if ( $pageActions && $pageActions['toolbar'] ) {
|
|
$html = $templateParser->processTemplate( 'PageActionsMenu', $pageActions );
|
|
}
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* Returns the 'Last edited' message, e.g. 'Last edited on...'
|
|
* @param array $data Data used to build the page
|
|
* @return string
|
|
*/
|
|
protected function getHistoryLinkHtml( $data ) {
|
|
$action = Action::getActionName( RequestContext::getMain() );
|
|
if ( isset( $data['historyLink'] ) && $action === 'view' ) {
|
|
$args = [
|
|
'clockIconClass' => MinervaUI::iconClass( 'clock', 'before' ),
|
|
'arrowIconClass' => MinervaUI::iconClass(
|
|
'arrow-gray', 'element', 'mw-ui-icon-small mf-mw-ui-icon-rotate-anti-clockwise indicator',
|
|
// Uses icon in MobileFrontend so must be prefixed mf.
|
|
// Without MobileFrontend it will not render.
|
|
// Rather than maintain 2 versions (and variants) of the arrow icon which can conflict
|
|
// with each othe and bloat CSS, we'll
|
|
// use the MobileFrontend one. Long term when T177432 and T160690 are resolved
|
|
// we should be able to use one icon definition and break this dependency.
|
|
'mf'
|
|
),
|
|
] + $data['historyLink'];
|
|
$templateParser = new TemplateParser( __DIR__ );
|
|
return $templateParser->processTemplate( 'history', $args );
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
protected function isFallbackEditor() {
|
|
$action = $this->getSkin()->getRequest()->getVal( 'action' );
|
|
return $action === 'edit';
|
|
}
|
|
|
|
/**
|
|
* Get page secondary actions
|
|
* @return string[]
|
|
*/
|
|
protected function getSecondaryActions() {
|
|
if ( $this->isFallbackEditor() ) {
|
|
return [];
|
|
}
|
|
|
|
return $this->data['secondary_actions'];
|
|
}
|
|
|
|
/**
|
|
* Get HTML representing secondary page actions like language selector
|
|
* @return string
|
|
*/
|
|
protected function getSecondaryActionsHtml() {
|
|
$baseClass = MinervaUI::buttonClass( '', 'button' );
|
|
/** @var SkinMinerva $skin */
|
|
$skin = $this->getSkin();
|
|
$html = '';
|
|
// no secondary actions on the user page
|
|
if ( $skin instanceof SkinMinerva && !$skin->getUserPageHelper()->isUserPage() ) {
|
|
foreach ( $this->getSecondaryActions() as $el ) {
|
|
if ( isset( $el['attributes']['class'] ) ) {
|
|
$el['attributes']['class'] .= ' ' . $baseClass;
|
|
} else {
|
|
$el['attributes']['class'] = $baseClass;
|
|
}
|
|
$html .= Html::element( 'a', $el['attributes'], $el['label'] );
|
|
}
|
|
}
|
|
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* Get the HTML for the content of a page
|
|
* @param array $data Data used to build the page
|
|
* @return string representing HTML of content
|
|
*/
|
|
protected function getContentHtml( $data ) {
|
|
if ( !$data[ 'unstyledContent' ] ) {
|
|
$content = Html::openElement( 'div', [
|
|
'id' => 'bodyContent',
|
|
'class' => 'content',
|
|
] );
|
|
$content .= $data[ 'bodytext' ];
|
|
if ( isset( $data['subject-page'] ) ) {
|
|
$content .= $data['subject-page'];
|
|
}
|
|
return $content . Html::closeElement( 'div' );
|
|
}
|
|
|
|
return $data[ 'bodytext' ];
|
|
}
|
|
|
|
/**
|
|
* Gets the main menu only on Special:MobileMenu.
|
|
* On other pages the menu is rendered via JS.
|
|
* @param array $data Data used to build the page
|
|
* @return string
|
|
*/
|
|
protected function getMainMenuHtml( $data ) {
|
|
if ( $this->isSpecialMobileMenuPage ) {
|
|
$templateParser = new TemplateParser(
|
|
__DIR__ . '/../../resources/skins.minerva.scripts/menu/' );
|
|
|
|
return $templateParser->processTemplate( 'menu', $data['mainMenu']['items'] );
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Render the entire page
|
|
* @param array $data Data used to build the page
|
|
* @todo replace with template engines
|
|
*/
|
|
protected function render( $data ) {
|
|
$skinOptions = MediaWikiServices::getInstance()->getService( 'Minerva.SkinOptions' );
|
|
$templateParser = new TemplateParser( __DIR__ );
|
|
|
|
$internalBanner = $data[ 'internalBanner' ];
|
|
$preBodyHtml = isset( $data['prebodyhtml'] ) ? $data['prebodyhtml'] : '';
|
|
$hasHeadingHolder = $internalBanner || $preBodyHtml || isset( $data['page_actions'] );
|
|
$hasPageActions = $this->hasPageActions( $data['skin']->getContext() );
|
|
|
|
// prepare template data
|
|
$templateData = [
|
|
'banners' => $data['banners'],
|
|
'wgScript' => $data['wgScript'],
|
|
'isAnon' => $data['username'] === null,
|
|
'search' => $data['search'],
|
|
'placeholder' => wfMessage( 'mobile-frontend-placeholder' ),
|
|
'headelement' => $data[ 'headelement' ],
|
|
'menuButton' => $data['mainMenu']['buttonHTML'],
|
|
'siteheading' => $data['footer-site-heading-html'],
|
|
'mainPageURL' => Title::newMainPage()->getLocalURL(),
|
|
// A button when clicked will submit the form
|
|
// This is used so that on tablet devices with JS disabled the search button
|
|
// passes the value of input to the search
|
|
// We avoid using input[type=submit] as these cannot be easily styled as mediawiki ui icons
|
|
// which is problematic in Opera Mini (see T140490)
|
|
'searchButton' => Html::rawElement( 'button', [
|
|
'id' => 'searchIcon',
|
|
'class' => MinervaUI::iconClass( 'magnifying-glass', 'element', 'skin-minerva-search-trigger' ),
|
|
], wfMessage( 'searchbutton' ) ),
|
|
'userNotificationsData' => $data['userNotificationsData'],
|
|
'mainmenuhtml' => $this->getMainMenuHtml( $data ),
|
|
'hasheadingholder' => $hasHeadingHolder,
|
|
'taglinehtml' => $data['taglinehtml'],
|
|
'internalBanner' => $internalBanner,
|
|
'prebodyhtml' => $preBodyHtml,
|
|
'headinghtml' => isset( $data['headinghtml'] ) ? $data['headinghtml'] : '',
|
|
'postheadinghtml' => isset( $data['postheadinghtml'] ) ? $data['postheadinghtml'] : '',
|
|
'haspageactions' => $hasPageActions,
|
|
'pageactionshtml' => $hasPageActions ? $this->getPageActionsHtml() : '',
|
|
'subtitle' => $data['subtitle'],
|
|
'contenthtml' => $this->getContentHtml( $data ),
|
|
'secondaryactionshtml' => $this->getSecondaryActionsHtml(),
|
|
'dataAfterContent' => $this->get( 'dataAfterContent' ),
|
|
'footer' => $this->getFooterTemplateData( $data ),
|
|
'isBeta' => $skinOptions->get( SkinOptions::OPTIONS_MOBILE_BETA ),
|
|
'tabs' => $this->showTalkTabs( $hasPageActions, $skinOptions ) &&
|
|
$skinOptions->get( SkinOptions::OPTIONS_TALK_AT_TOP ) ? [
|
|
'items' => array_values( $data['content_navigation']['namespaces'] ),
|
|
] : false,
|
|
];
|
|
// begin rendering
|
|
echo $templateParser->processTemplate( 'minerva', $templateData );
|
|
$this->printTrail();
|
|
?>
|
|
</body>
|
|
</html>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* @param IContextSource $context
|
|
* @return bool
|
|
*/
|
|
private function hasPageActions( IContextSource $context ) {
|
|
return !$this->isSpecialPage && !$this->isMainPage &&
|
|
Action::getActionName( $context ) === 'view';
|
|
}
|
|
|
|
/**
|
|
* @param bool $hasPageActions
|
|
* @param SkinOptions $skinOptions
|
|
* @return bool
|
|
*/
|
|
private function showTalkTabs( $hasPageActions, SkinOptions $skinOptions ) {
|
|
$hasTalkTabs = $hasPageActions && !$this->isMainPageTalk;
|
|
if ( !$hasTalkTabs && $this->isSpecialPage &&
|
|
$skinOptions->get( SkinOptions::OPTION_TABS_ON_SPECIALS ) ) {
|
|
$hasTalkTabs = true;
|
|
}
|
|
return $hasTalkTabs;
|
|
}
|
|
}
|