Prepare to break the server side MobileFrontend dependency on Minerva
Changes: * Minerva now maintains a MinervaUI - a simplified version of MobileUI that provides iconClass and buttonClass helpers. * Minerva now maintains its own ResourceLoaderParserMessageModule Remaining issues: * Main menu links to '#' * Unknown dependency errors are thrown due to the missing JS libraries e.g. mobile.watchstar thus JS based UI components are unusable e.g. search autocomplete, and edit button * Language button navigates to a missing special page without MobileFrontend (see T104660) Bug: T169569 Change-Id: I89e2e15faabab73b0cba91afc2f2c5e785edef29
This commit is contained in:
parent
67766539f9
commit
0dd994edda
107
includes/MinervaResourceLoaderParsedMessageModule.php
Normal file
107
includes/MinervaResourceLoaderParsedMessageModule.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* ResourceLoaderModule subclass for Minerva
|
||||
* Allows basic server side parsing of messages without arguments
|
||||
*/
|
||||
class MinervaResourceLoaderParsedMessageModule extends ResourceLoaderFileModule {
|
||||
/** @var array Saves a list of messages which have been marked as needing parsing. */
|
||||
protected $parsedMessages = [];
|
||||
/** @var array Saves a list of message keys used by this module. */
|
||||
protected $messages = [];
|
||||
/** @var array Saves the target for the module (e.g. desktop and mobile). */
|
||||
protected $targets = [ 'mobile', 'desktop' ];
|
||||
/** @var boolean Whether the module abuses getScript. */
|
||||
protected $hasHackedScriptMode = false;
|
||||
|
||||
/**
|
||||
* Registers core modules and runs registration hooks.
|
||||
* @param array $options List of options; if not given or empty,
|
||||
* an empty module will be constructed
|
||||
*/
|
||||
public function __construct( $options ) {
|
||||
foreach ( $options as $member => $option ) {
|
||||
switch ( $member ) {
|
||||
case 'messages':
|
||||
$this->processMessages( $option );
|
||||
$this->hasHackedScriptMode = true;
|
||||
// Prevent them being reinitialised when parent construct is called.
|
||||
unset( $options[$member] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parent::__construct( $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process messages which have been marked as needing parsing
|
||||
*
|
||||
* @param ResourceLoaderContext $context
|
||||
* @return string JavaScript code
|
||||
*/
|
||||
public function addParsedMessages( ResourceLoaderContext $context ) {
|
||||
if ( !$this->parsedMessages ) {
|
||||
return '';
|
||||
}
|
||||
$messages = [];
|
||||
foreach ( $this->parsedMessages as $key ) {
|
||||
$messages[ $key ] = $context->msg( $key )->parse();
|
||||
}
|
||||
return Xml::encodeJsCall( 'mw.messages.set', [ $messages ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Separate messages which have been marked as needing parsing from standard messages
|
||||
* @param array $messages Array of messages to process
|
||||
*/
|
||||
private function processMessages( $messages ) {
|
||||
foreach ( $messages as $key => $value ) {
|
||||
if ( is_array( $value ) ) {
|
||||
foreach ( $value as $directive ) {
|
||||
if ( $directive == 'parse' ) {
|
||||
$this->parsedMessages[] = $key;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->messages[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all scripts for a given context concatenated together including processed messages
|
||||
*
|
||||
* @param ResourceLoaderContext $context Context in which to generate script
|
||||
* @return string JavaScript code for $context
|
||||
*/
|
||||
public function getScript( ResourceLoaderContext $context ) {
|
||||
$script = parent::getScript( $context );
|
||||
return $this->addParsedMessages( $context ) . $script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL or URLs to load for this module's JS in debug mode.
|
||||
* @param ResourceLoaderContext $context
|
||||
* @return array list of urls
|
||||
* @see ResourceLoaderModule::getScriptURLsForDebug
|
||||
*/
|
||||
public function getScriptURLsForDebug( ResourceLoaderContext $context ) {
|
||||
if ( $this->hasHackedScriptMode ) {
|
||||
$derivative = new DerivativeResourceLoaderContext( $context );
|
||||
$derivative->setDebug( true );
|
||||
$derivative->setModules( [ $this->getName() ] );
|
||||
// @todo FIXME: Make this templates and update
|
||||
// makeModuleResponse so that it only outputs template code.
|
||||
// When this is done you can merge with parent array and
|
||||
// retain file names.
|
||||
$derivative->setOnly( 'scripts' );
|
||||
$rl = $derivative->getResourceLoader();
|
||||
$urls = [
|
||||
$rl->createLoaderURL( $this->getSource(), $derivative ),
|
||||
];
|
||||
} else {
|
||||
$urls = parent::getScriptURLsForDebug( $context );
|
||||
}
|
||||
return $urls;
|
||||
}
|
||||
}
|
53
includes/MinervaUI.php
Normal file
53
includes/MinervaUI.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MinervaUI.php
|
||||
*/
|
||||
|
||||
// FIXME: Use OOUI PHP when available.
|
||||
/**
|
||||
* Helper methods for generating parts of the UI.
|
||||
*/
|
||||
class MinervaUI {
|
||||
|
||||
/**
|
||||
* Get CSS classes for icons
|
||||
* @param string $iconName
|
||||
* @param string $iconType element or before
|
||||
* @param string $additionalClassNames additional class names you want to associate
|
||||
* with the iconed element
|
||||
* @return string class name for use with HTML element
|
||||
*/
|
||||
public static function iconClass( $iconName, $iconType = 'element', $additionalClassNames = '' ) {
|
||||
$base = 'mw-ui-icon';
|
||||
$modifiers = 'mw-ui-icon-' . $iconType;
|
||||
if ( $iconName ) {
|
||||
$modifiers .= ' mw-ui-icon-' . $iconName;
|
||||
}
|
||||
return $base . ' ' . $modifiers . ' ' . $additionalClassNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CSS classes for a mediawiki ui semantic element
|
||||
* @param string $base The base class
|
||||
* @param string $modifier Type of anchor (progressive, constructive, destructive)
|
||||
* @param string $additionalClassNames additional class names you want to associate
|
||||
* with the iconed element
|
||||
* @return string class name for use with HTML element
|
||||
*/
|
||||
public static function semanticClass( $base, $modifier, $additionalClassNames = '' ) {
|
||||
$modifier = empty( $modifier ) ? '' : 'mw-ui-' . $modifier;
|
||||
return $base . ' ' . $modifier . ' ' . $additionalClassNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CSS classes for buttons
|
||||
* @param string $modifier Type of button (progressive, constructive, destructive)
|
||||
* @param string $additionalClassNames additional class names you want to associate
|
||||
* with the button element
|
||||
* @return string class name for use with HTML element
|
||||
*/
|
||||
public static function buttonClass( $modifier = '', $additionalClassNames = '' ) {
|
||||
return self::semanticClass( 'mw-ui-button', $modifier, $additionalClassNames );
|
||||
}
|
||||
}
|
@ -109,8 +109,8 @@ class MinervaTemplate extends BaseTemplate {
|
||||
if ( isset( $data['historyLink'] ) && $action === 'view' ) {
|
||||
$historyLink = $data['historyLink'];
|
||||
$args = [
|
||||
'clockIconClass' => MobileUI::iconClass( 'clock-gray', 'before' ),
|
||||
'arrowIconClass' => MobileUI::iconClass(
|
||||
'clockIconClass' => MinervaUI::iconClass( 'clock-gray', 'before' ),
|
||||
'arrowIconClass' => MinervaUI::iconClass(
|
||||
'arrow-gray', 'element', 'mw-ui-icon-small mf-mw-ui-icon-rotate-anti-clockwise indicator' ),
|
||||
'isMainPage' => $this->getSkin()->getTitle()->isMainPage(),
|
||||
'link' => $historyLink['href'],
|
||||
@ -146,7 +146,7 @@ class MinervaTemplate extends BaseTemplate {
|
||||
* @return string
|
||||
*/
|
||||
protected function getSecondaryActionsHtml() {
|
||||
$baseClass = MobileUI::buttonClass( '', 'button' );
|
||||
$baseClass = MinervaUI::buttonClass( '', 'button' );
|
||||
$html = Html::openElement( 'div', [
|
||||
'class' => 'post-content',
|
||||
'id' => 'page-secondary-actions'
|
||||
@ -281,7 +281,7 @@ class MinervaTemplate extends BaseTemplate {
|
||||
// which is problematic in Opera Mini (see T140490)
|
||||
'searchButton' => Html::rawElement( 'button', [
|
||||
'id' => 'searchIcon',
|
||||
'class' => MobileUI::iconClass( 'magnifying-glass', 'element' ),
|
||||
'class' => MinervaUI::iconClass( 'magnifying-glass', 'element' ),
|
||||
], wfMessage( 'searchbutton' ) ),
|
||||
'secondaryButtonData' => $data['secondaryButtonData'],
|
||||
'mainmenuhtml' => $this->getMainMenuHtml( $data ),
|
||||
|
@ -236,7 +236,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
'data-section' => $section,
|
||||
// Note visibility of the edit section link button is controlled by .edit-page in ui.less so
|
||||
// we default to enabled even though this may not be true.
|
||||
'class' => MobileUI::iconClass( 'edit-enabled', 'element', 'edit-page' ),
|
||||
'class' => MinervaUI::iconClass( 'edit-enabled', 'element', 'edit-page' ),
|
||||
], $message );
|
||||
$html .= Html::closeElement( 'span' );
|
||||
return $html;
|
||||
@ -408,7 +408,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
[ 'returnto' => $currentTitle->getPrefixedText() ] );
|
||||
|
||||
$tpl->set( 'secondaryButtonData', [
|
||||
'notificationIconClass' => MobileUI::iconClass( 'notifications' ),
|
||||
'notificationIconClass' => MinervaUI::iconClass( 'notifications' ),
|
||||
'title' => $notificationsMsg,
|
||||
'url' => $url,
|
||||
'notificationCount' => $countLabel,
|
||||
@ -452,7 +452,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
->addComponent(
|
||||
$this->msg( 'mobile-frontend-main-menu-contributions' )->escaped(),
|
||||
SpecialPage::getTitleFor( 'Contributions', $user->getName() )->getLocalUrl(),
|
||||
MobileUI::iconClass( 'mf-contributions', 'before' ),
|
||||
MinervaUI::iconClass( 'mf-contributions', 'before' ),
|
||||
[ 'data-event-name' => 'contributions' ]
|
||||
);
|
||||
}
|
||||
@ -490,7 +490,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
'mobile-frontend-watchlist-purpose',
|
||||
$watchlistQuery
|
||||
),
|
||||
MobileUI::iconClass( 'mf-watchlist', 'before' ),
|
||||
MinervaUI::iconClass( 'mf-watchlist', 'before' ),
|
||||
[ 'data-event-name' => 'watchlist' ]
|
||||
);
|
||||
}
|
||||
@ -512,7 +512,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
$this->msg( 'mobile-frontend-main-menu-settings' )->escaped(),
|
||||
SpecialPage::getTitleFor( 'MobileOptions' )->
|
||||
getLocalUrl( [ 'returnto' => $returnToTitle ] ),
|
||||
MobileUI::iconClass( 'mf-settings', 'before' ),
|
||||
MinervaUI::iconClass( 'mf-settings', 'before' ),
|
||||
[ 'data-event-name' => 'settings' ]
|
||||
);
|
||||
|
||||
@ -527,7 +527,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
SpecialPage::getTitleFor( 'Preferences' ),
|
||||
'prefsnologintext2'
|
||||
),
|
||||
MobileUI::iconClass( 'mf-settings', 'before' ),
|
||||
MinervaUI::iconClass( 'mf-settings', 'before' ),
|
||||
[ 'data-event-name' => 'preferences' ]
|
||||
);
|
||||
}
|
||||
@ -613,7 +613,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
->addComponent(
|
||||
$this->msg( 'mobile-frontend-home-button' )->escaped(),
|
||||
Title::newMainPage()->getLocalUrl(),
|
||||
MobileUI::iconClass( 'mf-home', 'before' ),
|
||||
MinervaUI::iconClass( 'mf-home', 'before' ),
|
||||
[ 'data-event-name' => 'home' ]
|
||||
);
|
||||
|
||||
@ -622,7 +622,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
->addComponent(
|
||||
$this->msg( 'mobile-frontend-random-button' )->escaped(),
|
||||
SpecialPage::getTitleFor( 'Randompage' )->getLocalUrl() . '#/random',
|
||||
MobileUI::iconClass( 'mf-random', 'before' ),
|
||||
MinervaUI::iconClass( 'mf-random', 'before' ),
|
||||
[
|
||||
'id' => 'randomButton',
|
||||
'data-event-name' => 'random',
|
||||
@ -639,7 +639,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
->addComponent(
|
||||
$this->msg( 'mobile-frontend-main-menu-nearby' )->escaped(),
|
||||
SpecialPage::getTitleFor( 'Nearby' )->getLocalURL(),
|
||||
MobileUI::iconClass( 'mf-nearby', 'before', 'nearby' ),
|
||||
MinervaUI::iconClass( 'mf-nearby', 'before', 'nearby' ),
|
||||
[ 'data-event-name' => 'nearby' ]
|
||||
);
|
||||
}
|
||||
@ -689,13 +689,13 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
->addComponent(
|
||||
$username,
|
||||
Title::newFromText( $username, NS_USER )->getLocalUrl(),
|
||||
MobileUI::iconClass( 'mf-profile', 'before', 'truncated-text primary-action' ),
|
||||
MinervaUI::iconClass( 'mf-profile', 'before', 'truncated-text primary-action' ),
|
||||
[ 'data-event-name' => 'profile' ]
|
||||
)
|
||||
->addComponent(
|
||||
$this->msg( 'mobile-frontend-main-menu-logout' )->escaped(),
|
||||
$url,
|
||||
MobileUI::iconClass(
|
||||
MinervaUI::iconClass(
|
||||
'mf-logout', 'element', 'secondary-action truncated-text' ),
|
||||
[ 'data-event-name' => 'logout' ]
|
||||
);
|
||||
@ -711,7 +711,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
->addComponent(
|
||||
$this->msg( 'mobile-frontend-main-menu-login' )->escaped(),
|
||||
$url,
|
||||
MobileUI::iconClass( 'mf-anonymous', 'before' ),
|
||||
MinervaUI::iconClass( 'mf-anonymous', 'before' ),
|
||||
[ 'data-event-name' => 'login' ]
|
||||
);
|
||||
}
|
||||
@ -913,7 +913,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
Html::element( 'a', [
|
||||
'title' => $this->msg( 'mobile-frontend-main-menu-button-tooltip' ),
|
||||
'href' => $url,
|
||||
'class' => MobileUI::iconClass( 'mainmenu', 'element', 'main-menu-button' ),
|
||||
'class' => MinervaUI::iconClass( 'mainmenu', 'element', 'main-menu-button' ),
|
||||
'id' => 'mw-mf-main-menu-button',
|
||||
], $this->msg( 'mobile-frontend-main-menu-button-tooltip' ) )
|
||||
);
|
||||
@ -1105,7 +1105,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
'id' => 'ca-edit',
|
||||
'text' => '',
|
||||
'itemtitle' => $this->msg( 'mobile-frontend-pageaction-edit-tooltip' ),
|
||||
'class' => MobileUI::iconClass( 'edit-enabled', 'element' ),
|
||||
'class' => MinervaUI::iconClass( 'edit-enabled', 'element' ),
|
||||
'links' => [
|
||||
'edit' => [
|
||||
'href' => $title->getLocalURL( $editArgs )
|
||||
@ -1127,7 +1127,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
$baseResult = [
|
||||
'id' => 'ca-watch',
|
||||
// Use blank icon to reserve space for watchstar icon once JS loads
|
||||
'class' => MobileUI::iconClass( '', 'element', 'watch-this-article' ),
|
||||
'class' => MinervaUI::iconClass( '', 'element', 'watch-this-article' ),
|
||||
'is_js_only' => true
|
||||
];
|
||||
$title = $this->getTitle();
|
||||
@ -1170,7 +1170,7 @@ class SkinMinerva extends SkinTemplate implements ICustomizableSkin {
|
||||
return [
|
||||
'text' => '',
|
||||
'itemtitle' => $this->msg( 'mobile-frontend-language-article-heading' ),
|
||||
'class' => MobileUI::iconClass( 'language-switcher', 'element', $languageSwitcherClasses ),
|
||||
'class' => MinervaUI::iconClass( 'language-switcher', 'element', $languageSwitcherClasses ),
|
||||
'links' => $languageSwitcherLinks,
|
||||
'is_js_only' => false
|
||||
];
|
||||
|
@ -1,5 +1,7 @@
|
||||
{
|
||||
"AutoloadClasses": {
|
||||
"MinervaResourceLoaderParsedMessageModule": "includes/MinervaResourceLoaderParsedMessageModule.php",
|
||||
"MinervaUI": "includes/MinervaUI.php",
|
||||
"MinervaHooks": "includes/Minerva.hooks.php",
|
||||
"MinervaTemplate": "includes/skins/MinervaTemplate.php",
|
||||
"SkinMinerva": "includes/skins/SkinMinerva.php",
|
||||
@ -350,7 +352,7 @@
|
||||
]
|
||||
},
|
||||
"skins.minerva.editor": {
|
||||
"class": "MFResourceLoaderParsedMessageModule",
|
||||
"class": "MinervaResourceLoaderParsedMessageModule",
|
||||
"dependencies": [
|
||||
"mediawiki.util",
|
||||
"mediawiki.router",
|
||||
@ -426,7 +428,7 @@
|
||||
]
|
||||
},
|
||||
"skins.minerva.toggling": {
|
||||
"class": "MFResourceLoaderParsedMessageModule",
|
||||
"class": "MinervaResourceLoaderParsedMessageModule",
|
||||
"dependencies": [
|
||||
"mobile.toggle",
|
||||
"skins.minerva.icons.images.variants",
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace Tests\MediaWiki\Minerva;
|
||||
|
||||
use MediaWikiTestCase;
|
||||
use MobileUI;
|
||||
use MinervaUI;
|
||||
use MWTimestamp;
|
||||
use OutputPage;
|
||||
use QuickTemplate;
|
||||
@ -289,7 +289,7 @@ class SkinMinervaTest extends MediaWikiTestCase {
|
||||
$hasUnseen
|
||||
) {
|
||||
return [
|
||||
'notificationIconClass' => MobileUI::iconClass( 'notifications' ),
|
||||
'notificationIconClass' => MinervaUI::iconClass( 'notifications' ),
|
||||
'title' => $notificationsMsg,
|
||||
'url' => SpecialPage::getTitleFor( $notificationsTitle )
|
||||
->getLocalURL(
|
||||
|
Loading…
Reference in New Issue
Block a user