Monobook Mustache
Changes in HTML markup that fix various bugs and lead to consistencies with other skins: * firstHeading now has `dir` attribute * `tagline` message no longer parsed - plain text only - this is consistent with other skins * printfooter now child of #bodyContent * #ca-view is outputted (but hidden with CSS) * Order of attributes on #p-search-label changed * Search input form elements are no longer self closing * The #mw-searchButton element gains class mw-fallbackSearchButton * The generated-sidebar class is no longer present on sidebar portlets, consistent with other skins * The print link disappears when ElectronPdf is installed so there are not two print links. Changes in functionality: * Previously (in getCactions) a nomobile class would be added if less than 2 tabs. If not 1 tab, more would be appended. This is dropped. Bug: T285989 Change-Id: I03d0dc1dad23894e7e64ceeb8956692316265144
This commit is contained in:
parent
1810f569a9
commit
abe94aa408
9 changed files with 187 additions and 588 deletions
|
@ -11,6 +11,7 @@
|
|||
"monobook-jumptonavigation": "Jump to navigation",
|
||||
"monobook-jumptosearch": "Jump to search",
|
||||
"monobook-more-actions": "More",
|
||||
"cactions-mobile": "Page actions",
|
||||
"monobook-cactions-label": "Page actions",
|
||||
"monobook-notifications-link": "Notifications ($1)",
|
||||
"monobook-notifications-link-none": "Notifications"
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"monobook-jumptonavigation": "Accessibility link for jumping to the navigation links. Visually hidden by default.\n\nSee also\n* {{msg-mw|Navigation}}\n\n{{Identical|jumptonavigation}}\n\njay94ks:\nMaybe this translation context is duplicated. :)\nI've found the perfectly same thing even description also same.\n* MediaWiki:Vector-jumptonavigation/ko - This context must be filled out with same content.",
|
||||
"monobook-jumptosearch": "Accessibility link for jumping to the site search. Visually hidden by default.\n\nSee also\n* {{msg-mw|Search}}\n\n{{Identical|jumptosearch}}",
|
||||
"monobook-more-actions": "Label for the less-important or rarer actions that are hidden from the usual tabs on mobile interfaces (like moving the page, or for sysops deleting or protecting the page). {{Identical|More}}",
|
||||
"cactions-mobile": "Header for the content actions menu (tabs on the top of the page)",
|
||||
"monobook-cactions-label": "Header for the content actions menu (tabs on the top of the page)",
|
||||
"monobook-notifications-link": "Label for Extension:Notifications link in mobile personal toolbar\n\nParameters:\n* $1 - number of current alerts/notifications",
|
||||
"monobook-notifications-link-none": "Label for Extension:Notifications link in mobile personal toolbar when no notifications present\n{{Identical|Notification}}"
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace MonoBook;
|
|||
|
||||
use OutputPage;
|
||||
use Skin;
|
||||
use SkinTemplate;
|
||||
|
||||
class Hooks {
|
||||
/**
|
||||
|
@ -48,4 +49,51 @@ class Hooks {
|
|||
$bodyAttrs['class'] .= ' monobook-capitalize-all-nouns';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SkinTemplateNavigationUniversal hook handler
|
||||
*
|
||||
* @param SkinTemplate $skin
|
||||
* @param array &$content_navigation
|
||||
*/
|
||||
public static function onSkinTemplateNavigationUniversal( SkinTemplate $skin, array &$content_navigation ) {
|
||||
$title = $skin->getTitle();
|
||||
if ( $skin->getSkinName() === 'monobook' ) {
|
||||
$tabs = [];
|
||||
$namespaces = $content_navigation['namespaces'];
|
||||
foreach ( $namespaces as $nsid => $attribs ) {
|
||||
$id = $nsid . '-mobile';
|
||||
$tabs[$id] = [] + $attribs;
|
||||
$tabs[$id]['title'] = $attribs['text'];
|
||||
$tabs[$id]['id'] = $id;
|
||||
}
|
||||
|
||||
if ( !$title->isSpecialPage() ) {
|
||||
$tabs['more'] = [
|
||||
'text' => $skin->msg( 'monobook-more-actions' )->text(),
|
||||
'href' => '#p-cactions',
|
||||
'id' => 'ca-more'
|
||||
];
|
||||
}
|
||||
|
||||
$tabs['toolbox'] = [
|
||||
'text' => $skin->msg( 'toolbox' )->text(),
|
||||
'href' => '#p-tb',
|
||||
'id' => 'ca-tools',
|
||||
'title' => $skin->msg( 'toolbox' )->text()
|
||||
];
|
||||
|
||||
$languages = $skin->getLanguages();
|
||||
if ( count( $languages ) > 0 ) {
|
||||
$tabs['languages'] = [
|
||||
'text' => $skin->msg( 'otherlanguages' )->text(),
|
||||
'href' => '#p-lang',
|
||||
'id' => 'ca-languages',
|
||||
'title' => $skin->msg( 'otherlanguages' )->text()
|
||||
];
|
||||
}
|
||||
|
||||
$content_navigation['cactions-mobile'] = $tabs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,583 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* MonoBook nouveau.
|
||||
*
|
||||
* Translated from gwicke's previous TAL template version to remove
|
||||
* dependency on PHPTAL.
|
||||
*
|
||||
* 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
|
||||
* @ingroup Skins
|
||||
*/
|
||||
|
||||
namespace MonoBook;
|
||||
|
||||
use BaseTemplate;
|
||||
use Html;
|
||||
use Linker;
|
||||
use Sanitizer;
|
||||
|
||||
/**
|
||||
* @ingroup Skins
|
||||
*/
|
||||
class MonoBookTemplate extends BaseTemplate {
|
||||
|
||||
/**
|
||||
* Template filter callback for MonoBook skin.
|
||||
* Takes an associative array of data set from a SkinTemplate-based
|
||||
* class, and a wrapper for MediaWiki's localization database, and
|
||||
* outputs a formatted page.
|
||||
*/
|
||||
public function execute() {
|
||||
// Open html, body elements, etc
|
||||
$html = $this->get( 'headelement' );
|
||||
$html .= Html::openElement( 'div', [ 'id' => 'globalWrapper' ] );
|
||||
|
||||
$html .= Html::openElement( 'div', [ 'id' => 'column-content' ] );
|
||||
$html .= Html::rawElement( 'div', [ 'id' => 'content', 'class' => 'mw-body', 'role' => 'main' ],
|
||||
Html::element( 'a', [ 'id' => 'top' ] ) .
|
||||
$this->getIfExists( 'sitenotice', [
|
||||
'wrapper' => 'div',
|
||||
'parameters' => [ 'id' => 'siteNotice' ]
|
||||
] ) .
|
||||
$this->getIndicators() .
|
||||
$this->getIfExists( 'title', [
|
||||
'loose' => true,
|
||||
'wrapper' => 'h1',
|
||||
'parameters' => [
|
||||
'id' => 'firstHeading',
|
||||
'class' => 'firstHeading',
|
||||
'lang' => $this->getSkin()->getTitle()->getPageViewLanguage()->getHtmlCode()
|
||||
]
|
||||
] ) .
|
||||
Html::rawElement( 'div', [ 'id' => 'bodyContent', 'class' => 'monobook-body' ],
|
||||
Html::rawElement( 'div', [ 'id' => 'siteSub' ], $this->getMsg( 'tagline' )->parse() ) .
|
||||
Html::rawElement(
|
||||
'div',
|
||||
[ 'id' => 'contentSub', 'lang' => $this->get( 'userlang' ), 'dir' => $this->get( 'dir' ) ],
|
||||
$this->get( 'subtitle' )
|
||||
) .
|
||||
$this->getIfExists( 'undelete', [ 'wrapper' => 'div', 'parameters' => [
|
||||
'id' => 'contentSub2'
|
||||
] ] ) .
|
||||
$this->getIfExists( 'newtalk', [ 'wrapper' => 'div', 'parameters' => [
|
||||
'class' => 'usermessage'
|
||||
] ] ) .
|
||||
Html::element( 'div', [ 'id' => 'jump-to-nav' ] ) .
|
||||
Html::element( 'a', [ 'href' => '#column-one', 'class' => 'mw-jump-link' ],
|
||||
$this->getMsg( 'monobook-jumptonavigation' )->text()
|
||||
) .
|
||||
Html::element( 'a', [ 'href' => '#searchInput', 'class' => 'mw-jump-link' ],
|
||||
$this->getMsg( 'monobook-jumptosearch' )->text()
|
||||
) .
|
||||
'<!-- start content -->' .
|
||||
|
||||
$this->get( 'bodytext' ) .
|
||||
$this->getIfExists( 'catlinks' ) .
|
||||
|
||||
'<!-- end content -->' .
|
||||
$this->getClear()
|
||||
)
|
||||
);
|
||||
$html .= $this->getIfExists( 'dataAfterContent' ) . $this->getClear();
|
||||
$html .= Html::closeElement( 'div' );
|
||||
|
||||
$html .= Html::rawElement( 'div',
|
||||
[
|
||||
'id' => 'column-one',
|
||||
'lang' => $this->get( 'userlang' ),
|
||||
'dir' => $this->get( 'dir' )
|
||||
],
|
||||
Html::element( 'h2', [], $this->getMsg( 'navigation-heading' )->text() ) .
|
||||
$this->getCactions() .
|
||||
$this->getBox( 'personal', $this->getPersonalTools(), 'personaltools' ) .
|
||||
Html::rawElement( 'div', [ 'class' => 'portlet', 'id' => 'p-logo', 'role' => 'banner' ],
|
||||
Html::element( 'a',
|
||||
[
|
||||
'href' => $this->data['nav_urls']['mainpage']['href'],
|
||||
'class' => 'mw-wiki-logo',
|
||||
]
|
||||
+ Linker::tooltipAndAccesskeyAttribs( 'p-logo' )
|
||||
)
|
||||
) .
|
||||
Html::rawElement( 'div', [ 'id' => 'sidebar' ], $this->getRenderedSidebar() ) .
|
||||
$this->getMobileNavigationIcon(
|
||||
'sidebar',
|
||||
$this->getMsg( 'jumptonavigation' )->text()
|
||||
) .
|
||||
$this->getMobileNavigationIcon(
|
||||
'p-personal',
|
||||
$this->getMsg( 'monobook-jumptopersonal' )->text()
|
||||
) .
|
||||
$this->getMobileNavigationIcon(
|
||||
'globalWrapper',
|
||||
$this->getMsg( 'monobook-jumptotop' )->text()
|
||||
)
|
||||
);
|
||||
$html .= '<!-- end of the left (by default at least) column -->';
|
||||
|
||||
$html .= $this->getClear();
|
||||
$html .= $this->getSimpleFooter();
|
||||
$html .= Html::closeElement( 'div' );
|
||||
|
||||
$html .= $this->getTrail();
|
||||
|
||||
$html .= Html::closeElement( 'body' );
|
||||
$html .= Html::closeElement( 'html' );
|
||||
|
||||
// The unholy echo
|
||||
echo $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapped link to create a mobile toggle/jump icon
|
||||
* Needs to be an on-page link (as opposed to drawing something on the fly for an
|
||||
* onclick event) for no-js support.
|
||||
*
|
||||
* @param string $target link target
|
||||
* @param string $title icon title
|
||||
*
|
||||
* @return string html empty link block
|
||||
*/
|
||||
protected function getMobileNavigationIcon( $target, $title ) {
|
||||
return Html::element( 'a', [
|
||||
'href' => "#$target",
|
||||
'title' => $title,
|
||||
'class' => 'menu-toggle',
|
||||
'id' => "$target-toggle"
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the cactions (content actions) tabs, as well as a second set of spoof tabs for mobile
|
||||
*
|
||||
* @return string html
|
||||
*/
|
||||
protected function getCactions() {
|
||||
$html = '';
|
||||
$allTabs = $this->data['content_actions'];
|
||||
$tabCount = count( $allTabs );
|
||||
|
||||
// Normal cactions
|
||||
if ( $tabCount > 2 ) {
|
||||
$html .= $this->getBox( 'cactions', $allTabs, 'monobook-cactions-label' );
|
||||
} else {
|
||||
// Is redundant with spoof, hide normal cactions entirely in mobile
|
||||
$html .= $this->getBox( 'cactions', $allTabs, 'monobook-cactions-label',
|
||||
[ 'extra-classes' => 'nomobile' ]
|
||||
);
|
||||
}
|
||||
|
||||
// Mobile cactions tabs
|
||||
$tabs = $this->data['content_navigation']['namespaces'];
|
||||
foreach ( $tabs as $tab => $attribs ) {
|
||||
$tabs[$tab]['id'] = $attribs['id'] . '-mobile';
|
||||
$tabs[$tab]['title'] = $attribs['text'];
|
||||
}
|
||||
|
||||
if ( $tabCount !== 1 ) {
|
||||
// Is not special page or stuff, append a 'more'
|
||||
$tabs['more'] = [
|
||||
'text' => $this->getMsg( 'monobook-more-actions' )->text(),
|
||||
'href' => '#p-cactions',
|
||||
'id' => 'ca-more'
|
||||
];
|
||||
}
|
||||
$tabs['toolbox'] = [
|
||||
'text' => $this->getMsg( 'toolbox' )->text(),
|
||||
'href' => '#p-tb',
|
||||
'id' => 'ca-tools',
|
||||
'title' => $this->getMsg( 'toolbox' )->text()
|
||||
];
|
||||
|
||||
$languages = $this->data['sidebar']['LANGUAGES'];
|
||||
if ( $languages !== false ) {
|
||||
$tabs['languages'] = [
|
||||
'text' => $this->getMsg( 'otherlanguages' )->text(),
|
||||
'href' => '#p-lang',
|
||||
'id' => 'ca-languages',
|
||||
'title' => $this->getMsg( 'otherlanguages' )->text()
|
||||
];
|
||||
}
|
||||
|
||||
$html .= $this->getBox( 'cactions-mobile', $tabs, 'monobook-cactions-label' );
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the full sidebar
|
||||
*
|
||||
* @return string html
|
||||
* @suppress PhanTypeMismatchArgument $content is an array
|
||||
* even though we are comparing it to boolean
|
||||
*/
|
||||
protected function getRenderedSidebar() {
|
||||
$sidebar = $this->data['sidebar'];
|
||||
$html = '';
|
||||
$languagesHTML = '';
|
||||
|
||||
if ( !isset( $sidebar['SEARCH'] ) ) {
|
||||
$sidebar['SEARCH'] = true;
|
||||
}
|
||||
|
||||
foreach ( $sidebar as $boxName => $content ) {
|
||||
if ( $content === false ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Numeric strings gets an integer when set as key, cast back - T73639
|
||||
$boxName = (string)$boxName;
|
||||
|
||||
if ( $boxName == 'SEARCH' ) {
|
||||
$html .= $this->getSearchBox();
|
||||
} elseif ( $boxName == 'TOOLBOX' ) {
|
||||
$html .= $this->getToolboxBox( $content );
|
||||
} elseif ( $boxName == 'LANGUAGES' ) {
|
||||
$languagesHTML = $this->getLanguageBox( $content );
|
||||
} else {
|
||||
$html .= $this->getBox(
|
||||
$boxName,
|
||||
$content,
|
||||
null,
|
||||
[ 'extra-classes' => 'generated-sidebar' ]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Output language portal last given it can be long
|
||||
// on articles which support multiple languages (T254546)
|
||||
return $html . $languagesHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the search button
|
||||
*
|
||||
* @return string html
|
||||
*/
|
||||
protected function getSearchBox() {
|
||||
$html = '';
|
||||
|
||||
$optionButtons = "\u{00A0} " . $this->makeSearchButton(
|
||||
'fulltext',
|
||||
[ 'id' => 'mw-searchButton', 'class' => 'searchButton' ]
|
||||
);
|
||||
$searchInputId = 'searchInput';
|
||||
$searchForm = Html::rawElement( 'form', [
|
||||
'action' => $this->get( 'wgScript' ),
|
||||
'id' => 'searchform'
|
||||
],
|
||||
Html::hidden( 'title', $this->get( 'searchtitle' ) ) .
|
||||
$this->makeSearchInput( [ 'id' => $searchInputId ] ) .
|
||||
$this->makeSearchButton( 'go', [ 'id' => 'searchButton', 'class' => 'searchButton' ] ) .
|
||||
$optionButtons
|
||||
);
|
||||
|
||||
$html .= $this->getBox( 'search', $searchForm, null, [
|
||||
'search-input-id' => $searchInputId,
|
||||
'role' => 'search',
|
||||
'body-id' => 'searchBody'
|
||||
] );
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the toolbox, complete with all three old hooks
|
||||
*
|
||||
* @param array $toolboxItems
|
||||
* @return string html
|
||||
*/
|
||||
protected function getToolboxBox( $toolboxItems ) {
|
||||
$html = '';
|
||||
|
||||
$html .= $this->getBox( 'tb', $toolboxItems, 'toolbox' );
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the languages box
|
||||
*
|
||||
* @param array $languages Interwiki language links
|
||||
* @return string html
|
||||
*/
|
||||
protected function getLanguageBox( $languages ) {
|
||||
$html = '';
|
||||
$name = 'lang';
|
||||
|
||||
if (
|
||||
$languages !== [] ||
|
||||
// Check getAfterPortlet to make sure the languages are shown
|
||||
// when empty but something has been injected in the portal. (T252841)
|
||||
$this->getAfterPortletHTML( $name )
|
||||
) {
|
||||
$html .= $this->getBox( $name, $languages, 'otherlanguages' );
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a sidebar box using getPortlet(); prefill some common stuff
|
||||
*
|
||||
* @param string $name
|
||||
* @param array|string $contents
|
||||
* @param-taint $contents escapes_htmlnoent
|
||||
* @param null|string|array|bool $msg
|
||||
* @param array $setOptions
|
||||
*
|
||||
* @return string html
|
||||
*/
|
||||
protected function getBox( $name, $contents, $msg = null, $setOptions = [] ) {
|
||||
$options = array_merge( [
|
||||
'class' => 'portlet',
|
||||
'body-class' => 'pBody',
|
||||
'text-wrapper' => ''
|
||||
], $setOptions );
|
||||
|
||||
return $this->getPortlet( $name, $contents, $msg, $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a block of navigation links with a header
|
||||
*
|
||||
* @param string $name
|
||||
* @param array|string $content array of links for use with makeListItem, or a block of text
|
||||
* @param null|string|array $msg
|
||||
* @param array $setOptions random crap to rename/do/whatever
|
||||
*
|
||||
* @return string html
|
||||
* @suppress PhanTypeMismatchArgumentNullable Many false positives
|
||||
*/
|
||||
protected function getPortlet( $name, $content, $msg = null, $setOptions = [] ) {
|
||||
// random stuff to override with any provided options
|
||||
$options = array_merge( [
|
||||
// handle role=search a little differently
|
||||
'role' => 'navigation',
|
||||
'search-input-id' => 'searchInput',
|
||||
// extra classes/ids
|
||||
'id' => 'p-' . $name,
|
||||
'class' => 'mw-portlet',
|
||||
'extra-classes' => '',
|
||||
'body-id' => null,
|
||||
'body-class' => 'mw-portlet-body',
|
||||
'body-extra-classes' => '',
|
||||
// wrapper for individual list items
|
||||
'text-wrapper' => [ 'tag' => 'span' ],
|
||||
], $setOptions );
|
||||
|
||||
// Handle the different $msg possibilities
|
||||
if ( $msg === null ) {
|
||||
$msg = $name;
|
||||
$msgParams = [];
|
||||
} elseif ( is_array( $msg ) ) {
|
||||
$msgString = array_shift( $msg );
|
||||
$msgParams = $msg;
|
||||
$msg = $msgString;
|
||||
} else {
|
||||
$msgParams = [];
|
||||
}
|
||||
$msgObj = $this->getMsg( $msg, $msgParams );
|
||||
if ( $msgObj->exists() ) {
|
||||
$msgString = $msgObj->parse();
|
||||
} else {
|
||||
$msgString = htmlspecialchars( $msg );
|
||||
}
|
||||
|
||||
$labelId = Sanitizer::escapeIdForAttribute( "p-$name-label" );
|
||||
|
||||
if ( is_array( $content ) ) {
|
||||
$contentText = Html::openElement( 'ul',
|
||||
[ 'lang' => $this->get( 'userlang' ), 'dir' => $this->get( 'dir' ) ]
|
||||
);
|
||||
foreach ( $content as $key => $item ) {
|
||||
if ( is_array( $options['text-wrapper'] ) ) {
|
||||
$contentText .= $this->makeListItem(
|
||||
$key,
|
||||
$item,
|
||||
[ 'text-wrapper' => $options['text-wrapper'] ]
|
||||
);
|
||||
} else {
|
||||
$contentText .= $this->makeListItem(
|
||||
$key,
|
||||
$item
|
||||
);
|
||||
}
|
||||
}
|
||||
$contentText .= Html::closeElement( 'ul' );
|
||||
} else {
|
||||
$contentText = $content;
|
||||
}
|
||||
|
||||
// Special handling for role=search
|
||||
$divOptions = [
|
||||
'role' => $options['role'],
|
||||
'class' => $this->mergeClasses( $options['class'], $options['extra-classes'] ),
|
||||
'id' => Sanitizer::escapeIdForAttribute( $options['id'] ),
|
||||
'title' => Linker::titleAttrib( $options['id'] )
|
||||
];
|
||||
if ( $options['role'] !== 'search' ) {
|
||||
$divOptions['aria-labelledby'] = $labelId;
|
||||
}
|
||||
$labelOptions = [
|
||||
'id' => $labelId,
|
||||
'lang' => $this->get( 'userlang' ),
|
||||
'dir' => $this->get( 'dir' )
|
||||
];
|
||||
if ( $options['role'] == 'search' ) {
|
||||
$msgString = Html::rawElement( 'label', [ 'for' => $options['search-input-id'] ], $msgString );
|
||||
}
|
||||
|
||||
$bodyDivOptions = [
|
||||
'class' => $this->mergeClasses( $options['body-class'], $options['body-extra-classes'] )
|
||||
];
|
||||
if ( is_string( $options['body-id'] ) ) {
|
||||
$bodyDivOptions['id'] = $options['body-id'];
|
||||
}
|
||||
|
||||
$html = Html::rawElement( 'div', $divOptions,
|
||||
Html::rawElement( 'h3', $labelOptions, $msgString ) .
|
||||
Html::rawElement( 'div', $bodyDivOptions,
|
||||
$contentText . $this->getAfterPortletHTML( $name )
|
||||
)
|
||||
);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for getPortlet
|
||||
*
|
||||
* Merge all provided css classes into a single array
|
||||
* Account for possible different input methods matching what Html::element stuff takes
|
||||
*
|
||||
* @param string|array $class base portlet/body class
|
||||
* @param string|array $extraClasses any extra classes to also include
|
||||
*
|
||||
* @return array all classes to apply
|
||||
*/
|
||||
protected function mergeClasses( $class, $extraClasses ) {
|
||||
if ( !is_array( $class ) ) {
|
||||
$class = [ $class ];
|
||||
}
|
||||
if ( !is_array( $extraClasses ) ) {
|
||||
$extraClasses = [ $extraClasses ];
|
||||
}
|
||||
|
||||
return array_merge( $class, $extraClasses );
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple wrapper for random if-statement-wrapped $this->data things
|
||||
*
|
||||
* @param string $object name of thing
|
||||
* @param array $setOptions
|
||||
*
|
||||
* @return string html
|
||||
*/
|
||||
protected function getIfExists( $object, $setOptions = [] ) {
|
||||
$options = [
|
||||
'loose' => false,
|
||||
'wrapper' => 'none',
|
||||
'parameters' => []
|
||||
];
|
||||
foreach ( $setOptions as $key => $value ) {
|
||||
$options[$key] = $value;
|
||||
}
|
||||
|
||||
$html = '';
|
||||
|
||||
if ( ( $options['loose'] && $this->data[$object] != '' ) ||
|
||||
( !$options['loose'] && $this->data[$object] ) ) {
|
||||
if ( $options['wrapper'] == 'none' ) {
|
||||
$html .= $this->get( $object );
|
||||
} else {
|
||||
$html .= Html::rawElement(
|
||||
$options['wrapper'],
|
||||
$options['parameters'],
|
||||
$this->get( $object )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderer for getFooterIcons and getFooterLinks as a generic footer block
|
||||
*
|
||||
* @return string html
|
||||
*/
|
||||
protected function getSimpleFooter() {
|
||||
$validFooterIcons = $this->get( 'footericons' );
|
||||
$validFooterLinks = $this->getFooterLinks( 'flat' );
|
||||
|
||||
$html = '';
|
||||
|
||||
$html .= Html::openElement( 'div', [
|
||||
'id' => 'footer',
|
||||
'class' => 'mw-footer',
|
||||
'role' => 'contentinfo',
|
||||
'lang' => $this->get( 'userlang' ),
|
||||
'dir' => $this->get( 'dir' )
|
||||
] );
|
||||
|
||||
foreach ( $validFooterIcons as $blockName => $footerIcons ) {
|
||||
$html .= Html::openElement( 'div', [
|
||||
'id' => Sanitizer::escapeIdForAttribute( "f-{$blockName}ico" ),
|
||||
'class' => 'footer-icons'
|
||||
] );
|
||||
foreach ( $footerIcons as $icon ) {
|
||||
$html .= $this->getSkin()->makeFooterIcon( $icon );
|
||||
}
|
||||
$html .= Html::closeElement( 'div' );
|
||||
}
|
||||
if ( count( $validFooterLinks ) > 0 ) {
|
||||
$html .= Html::openElement( 'ul', [ 'id' => 'f-list' ] );
|
||||
foreach ( $validFooterLinks as $aLink ) {
|
||||
$html .= Html::rawElement(
|
||||
'li',
|
||||
[ 'id' => Sanitizer::escapeIdForAttribute( $aLink ) ],
|
||||
$this->get( $aLink )
|
||||
);
|
||||
}
|
||||
$html .= Html::closeElement( 'ul' );
|
||||
}
|
||||
$html .= Html::closeElement( 'div' );
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets after portal HTML and wraps it with div and class
|
||||
*
|
||||
* @param string $name
|
||||
* @return string html
|
||||
*/
|
||||
private function getAfterPortletHTML( $name ) {
|
||||
$content = $this->getSkin()->getAfterPortlet( $name );
|
||||
if ( $content !== '' ) {
|
||||
return Html::rawElement(
|
||||
'div',
|
||||
[ 'class' => [ 'after-portlet', 'after-portlet-' . $name ] ],
|
||||
$content
|
||||
);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
@import 'variables.less';
|
||||
|
||||
// remove duplicates we're not using here
|
||||
#sidebar .generated-sidebar,
|
||||
#p-search-mobilejs,
|
||||
#p-tb-mobilejs,
|
||||
#p-lang-mobilejs,
|
||||
|
@ -9,6 +8,12 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
#sidebar #p-tb,
|
||||
#sidebar #p-lang,
|
||||
#sidebar #p-search {
|
||||
display: block;
|
||||
}
|
||||
|
||||
// popouts
|
||||
#p-cactions,
|
||||
#p-personal,
|
||||
|
|
|
@ -310,6 +310,8 @@ li#ca-print {
|
|||
margin-left: 1.6em;
|
||||
}
|
||||
|
||||
/* Historically not present in Monobook skin */
|
||||
#p-cactions li#ca-view,
|
||||
/*
|
||||
** mobile toggles; not used here
|
||||
*/
|
||||
|
|
23
skin.json
23
skin.json
|
@ -15,14 +15,28 @@
|
|||
},
|
||||
"ValidSkinNames": {
|
||||
"monobook": {
|
||||
"class": "SkinTemplate",
|
||||
"class": "SkinMustache",
|
||||
"args": [
|
||||
{
|
||||
"name": "monobook",
|
||||
"responsive": true,
|
||||
"templateDirectory": "templates/",
|
||||
"scripts": [ "skins.monobook.scripts" ],
|
||||
"styles": [ "skins.monobook.styles" ],
|
||||
"responsive": true,
|
||||
"template": "MonoBook\\MonoBookTemplate"
|
||||
"messages": [
|
||||
"tagline",
|
||||
"nstab-main",
|
||||
"nstab-talk",
|
||||
"monobook-more-actions",
|
||||
"otherlanguages",
|
||||
"toolbox",
|
||||
"navigation-heading",
|
||||
"monobook-jumptotop",
|
||||
"monobook-jumptopersonal",
|
||||
"monobook-jumptosearch",
|
||||
"monobook-cactions-label",
|
||||
"monobook-jumptonavigation"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -34,7 +48,8 @@
|
|||
"monobook": "resources/mediawiki.less"
|
||||
},
|
||||
"Hooks": {
|
||||
"OutputPageBodyAttributes": "MonoBook\\Hooks::onOutputPageBodyAttributes"
|
||||
"OutputPageBodyAttributes": "MonoBook\\Hooks::onOutputPageBodyAttributes",
|
||||
"SkinTemplateNavigation::Universal": "MonoBook\\Hooks::onSkinTemplateNavigationUniversal"
|
||||
},
|
||||
"MessagesDirs": {
|
||||
"MonoBook": [
|
||||
|
|
10
templates/Portlet.mustache
Normal file
10
templates/Portlet.mustache
Normal file
|
@ -0,0 +1,10 @@
|
|||
{{! monobook uses a `portlet` class. Standardisation of classes will come later }}
|
||||
<div role="navigation" class="portlet {{class}}"
|
||||
id="{{id}}" aria-labelledby="{{id}}-label">
|
||||
<h3 id="{{id}}-label" {{{html-user-language-attributes}}}>{{label}}</h3>
|
||||
<div class="pBody">
|
||||
<ul {{{html-user-language-attributes}}}>{{{html-items}}}{{!
|
||||
}}</ul>
|
||||
{{{html-after-portal}}}
|
||||
</div>
|
||||
</div>
|
100
templates/skin.mustache
Normal file
100
templates/skin.mustache
Normal file
|
@ -0,0 +1,100 @@
|
|||
<div id="globalWrapper">
|
||||
<div id="column-content">
|
||||
<div id="content" class="mw-body" role="main">
|
||||
<a id="top"></a>
|
||||
<div id="siteNotice">{{{html-site-notice}}}</div>
|
||||
<div class="mw-indicators">
|
||||
{{#array-indicators}}
|
||||
<div id="{{id}}" class="mw-indicator">{{{html}}}</div>
|
||||
{{/array-indicators}}
|
||||
</div>
|
||||
<h1 id="firstHeading" class="firstHeading"
|
||||
{{{html-user-language-attributes}}}>{{{html-title}}}</h1>
|
||||
<div id="bodyContent" class="monobook-body">
|
||||
<div id="siteSub">{{ msg-tagline }}</div>
|
||||
<div id="contentSub" {{{html-user-language-attributes}}}>{{{html-subtitle}}}</div>
|
||||
{{#html-undelete-link}}<div id="contentSub2">{{{.}}}</div>{{/html-undelete-link}}{{{html-newtalk}}}
|
||||
<div id="jump-to-nav"></div><a href="#column-one" class="mw-jump-link">{{msg-monobook-jumptonavigation}}</a><a href="#searchInput" class="mw-jump-link">{{msg-monobook-jumptosearch}}</a>
|
||||
<!-- start content -->
|
||||
{{{html-body-content}}}
|
||||
{{{html-categories}}}
|
||||
<!-- end content -->
|
||||
<div class="visualClear"></div>
|
||||
</div>
|
||||
</div>{{{html-after-content}}}
|
||||
<div class="visualClear"></div>
|
||||
</div>
|
||||
<div id="column-one" {{{html-user-language-attributes}}}>
|
||||
{{#data-portlets}}
|
||||
<h2>{{msg-navigation-heading}}</h2>
|
||||
<div role="navigation" class="portlet" id="p-cactions" aria-labelledby="p-cactions-label">
|
||||
<h3 id="p-cactions-label" {{{html-user-language-attributes}}}>{{msg-monobook-cactions-label}}</h3>
|
||||
<div class="pBody">
|
||||
<ul {{{html-user-language-attributes}}}>
|
||||
{{! comments used to avoid additional whitespace}}
|
||||
{{{data-namespaces.html-items}}}{{!
|
||||
}}{{{data-views.html-items}}}{{!
|
||||
}}{{{data-actions.html-items}}}
|
||||
{{{data-variants.html-items}}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{#data-cactions-mobile}}{{>Portlet}}{{/data-cactions-mobile}}
|
||||
{{#data-personal}}
|
||||
<div role="navigation" class="portlet" id="{{id}}" aria-labelledby="{{id}}-label">
|
||||
<h3 id="{{id}}-label" {{{html-user-language-attributes}}}>{{label}}</h3>
|
||||
<div class="pBody">
|
||||
<ul {{{html-user-language-attributes}}}>
|
||||
{{{html-items}}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{/data-personal}}
|
||||
<div class="portlet" id="p-logo" role="banner">
|
||||
<a href="{{link-mainpage}}" class="mw-wiki-logo"></a>
|
||||
</div>
|
||||
<div id="sidebar">
|
||||
{{/data-portlets}}
|
||||
{{#data-portlets-sidebar.data-portlets-first}}{{>Portlet}}{{/data-portlets-sidebar.data-portlets-first}}
|
||||
{{#data-search-box}}
|
||||
<div role="search" class="portlet" id="p-search">
|
||||
<h3 id="p-search-label" dir="ltr" lang="en-GB"><label for="searchInput">{{msg-search}}</label></h3>
|
||||
<div class="pBody" id="searchBody">
|
||||
<form action="{{form-action}}" id="searchform"><input type="hidden" value="{{page-title}}" name="title">{{{html-input}}}{{{html-button-search}}} {{{html-button-search-fallback}}}</form>
|
||||
</div>
|
||||
</div>
|
||||
{{/data-search-box}}
|
||||
{{#data-portlets-sidebar.array-portlets-rest}}{{>Portlet}}{{/data-portlets-sidebar.array-portlets-rest}}
|
||||
{{#data-portlets.data-languages}}{{>Portlet}}{{/data-portlets.data-languages}}
|
||||
</div>
|
||||
{{! previously SkinMonobook::getMobileNavigationIcon }}
|
||||
<a href="#sidebar" title="{{msg-monobook-jumptonavigation}}"
|
||||
class="menu-toggle" id="sidebar-toggle"></a>
|
||||
<a href="#p-personal" title="{{msg-monobook-jumptopersonal}}"
|
||||
class="menu-toggle" id="p-personal-toggle"></a>
|
||||
<a href="#globalWrapper" title="{{msg-monobook-jumptotop}}"
|
||||
class="menu-toggle" id="globalWrapper-toggle"></a>
|
||||
</div>
|
||||
<!-- end of the left (by default at least) column -->
|
||||
<div class="visualClear"></div>
|
||||
<div id="footer" class="mw-footer" role="contentinfo"
|
||||
{{{html-user-language-attributes}}}>
|
||||
{{#data-footer}}
|
||||
{{#data-icons}}
|
||||
{{#array-items}}
|
||||
<div id="f-{{name}}ico" class="footer-icons">
|
||||
{{{html}}}
|
||||
</div>
|
||||
{{/array-items}}
|
||||
{{/data-icons}}
|
||||
<ul id="f-list">
|
||||
{{#data-info}}
|
||||
{{#array-items}}<li id="{{name}}">{{{html}}}</li>{{/array-items}}
|
||||
{{/data-info}}{{! no whitespace
|
||||
}}{{#data-places}}{{! no whitespace
|
||||
}}{{#array-items}}<li id="{{name}}">{{{html}}}</li>{{/array-items}}
|
||||
{{/data-places}}
|
||||
</ul>
|
||||
{{/data-footer}}
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in a new issue