Introduce SkinComponent and SkinComponentLogos

In preparation for refactoring SkinTemplate so that SkinMustache
extends Skin rather than SkinTemplate, we take the opportunity
to reorganize the skin code around the concept of components.

Going forward a skin will consist of multiple components, each
of which must return template data that can be passed to an
associated template.

This will result in code that is easier to work with, compared
with the existing 3000 line skin class.

This is the beginning of that journey. Other components will follow
while maintaining backwards compatibility

Bug: T263213
Change-Id: Ib62724c24601e04aa13ab09b3242e70d7d6436ca
This commit is contained in:
jdlrobson 2021-10-13 11:14:23 -07:00 committed by Jon Robson
parent 7db80245b7
commit f7693089a8
7 changed files with 240 additions and 23 deletions

View file

@ -1090,6 +1090,9 @@ $wgAutoloadLocalClasses = [
'MediaWiki\\Parser\\RevisionOutputCache' => __DIR__ . '/includes/parser/RevisionOutputCache.php',
'MediaWiki\\ProcOpenError' => __DIR__ . '/includes/exception/ProcOpenError.php',
'MediaWiki\\ShellDisabledError' => __DIR__ . '/includes/exception/ShellDisabledError.php',
'MediaWiki\\Skin\\SkinComponent' => __DIR__ . '/includes/skins/components/SkinComponent.php',
'MediaWiki\\Skin\\SkinComponentLogo' => __DIR__ . '/includes/skins/components/SkinComponentLogo.php',
'MediaWiki\\Skin\\SkinComponentRegistry' => __DIR__ . '/includes/skins/components/SkinComponentRegistry.php',
'MediaWiki\\Skins\\Hook\\SkinAfterPortletHook' => __DIR__ . '/includes/skins/Hook/SkinAfterPortletHook.php',
'MediaWiki\\Skins\\Hook\\SkinPageReadyConfigHook' => __DIR__ . '/includes/skins/Hook/SkinPageReadyConfigHook.php',
'MediaWiki\\Special\\SpecialPageFactory' => __DIR__ . '/includes/specialpage/SpecialPageFactory.php',

View file

@ -24,6 +24,7 @@ use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionLookup;
use MediaWiki\Revision\RevisionStore;
use MediaWiki\Skin\SkinComponentRegistry;
use MediaWiki\User\UserIdentity;
use MediaWiki\User\UserIdentityValue;
use Wikimedia\WrappedString;
@ -83,6 +84,11 @@ abstract class Skin extends ContextSource {
/** @var array|null Cache for buildSidebar() */
private $sidebar;
/**
* @var SkinComponentRegistry Initialised in constructor.
*/
private $componentRegistry = null;
/**
* Get the current major version of Skin. This is used to manage changes
* to underlying data and for providing support for older and new versions of code.
@ -229,6 +235,11 @@ abstract class Skin extends ContextSource {
'is-mainpage' => $isMainPage,
'is-specialpage' => $title->isSpecialPage(),
];
$components = $this->componentRegistry->getComponents();
foreach ( $components as $componentName => $component ) {
$data['data-' . $componentName] = $component->getTemplateData();
}
return $data;
}
@ -317,6 +328,9 @@ abstract class Skin extends ContextSource {
$this->options = $options;
$this->skinname = $name;
}
$this->componentRegistry = new SkinComponentRegistry(
$this
);
}
/**

View file

@ -198,7 +198,6 @@ class SkinTemplate extends Skin {
return parent::getTemplateData() + [
// Data objects
'data-search-box' => $this->buildSearchProps(),
'data-logos' => $this->getLogoData(),
] + $this->getPortletsTemplateData() + $this->getFooterTemplateData();
}
@ -905,27 +904,6 @@ class SkinTemplate extends Skin {
];
}
/**
* @return array of logo data localised to the current language variant
*/
private function getLogoData(): array {
$logoData = ResourceLoaderSkinModule::getAvailableLogos( $this->getConfig() );
// check if the logo supports variants
$variantsLogos = $logoData['variants'] ?? null;
if ( $variantsLogos ) {
$preferred = $this->getOutput()->getTitle()
->getPageViewLanguage()->getCode();
$variantOverrides = $variantsLogos[$preferred] ?? null;
// Overrides the logo
if ( $variantOverrides ) {
foreach ( $variantOverrides as $key => $val ) {
$logoData[$key] = $val;
}
}
}
return $logoData;
}
/**
* @return array
*/

View file

@ -0,0 +1,34 @@
<?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
*/
namespace MediaWiki\Skin;
/**
* @internal for use inside Skin and SkinTemplate classes only
* @unstable
*/
interface SkinComponent {
/**
* This returns all the data that is needed to the component.
* Returned array must be seralized. This will be passed directly
* to a template (usually Mustache) for rendering.
*
* @return array Data related to component required to render.
*/
public function getTemplateData(): array;
}

View file

@ -0,0 +1,85 @@
<?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
*/
namespace MediaWiki\Skin;
use Config;
use ResourceLoaderSkinModule;
use Title;
/**
* @internal for use inside Skin and SkinTemplate classes only
* @unstable
*/
class SkinComponentLogo implements SkinComponent {
/** @var Config */
private $config;
/** @var Title */
private $title;
/**
* @param Config $config
* @param Title|null $title
*/
public function __construct( Config $config, ?Title $title ) {
$this->config = $config;
$this->title = $title;
}
/**
* @return Title|null
*/
private function getTitle(): ?Title {
return $this->title;
}
/**
* @return Config
*/
private function getConfig(): Config {
return $this->config;
}
/**
* @inheritDoc
* Since 1.35 (all fields optional):
* - string 1x Path to a square icon at 1x resolution
* - string 2x Path to a square icon at 2x resolution
* - string icon Path to a square icon
* - array wordmark with `src`, `width`, `height` and `style` keys.
* - array tagline with `src`, `width`, `height` and `style` keys.
*/
public function getTemplateData(): array {
$logoData = ResourceLoaderSkinModule::getAvailableLogos( $this->getConfig() );
// check if the logo supports variants
$variantsLogos = $logoData['variants'] ?? null;
$title = $this->getTitle();
if ( $variantsLogos && $title ) {
$preferred = $title
->getPageViewLanguage()->getCode();
$variantOverrides = $variantsLogos[$preferred] ?? null;
// Overrides the logo
if ( $variantOverrides ) {
foreach ( $variantOverrides as $key => $val ) {
$logoData[$key] = $val;
}
}
}
return $logoData;
}
}

View file

@ -0,0 +1,103 @@
<?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
*/
namespace MediaWiki\Skin;
use RuntimeException;
use Skin;
/**
* @internal for use inside Skin and SkinTemplate classes only
* @unstable
*/
class SkinComponentRegistry {
/** @var SkinComponent[]|null null if not initialized. */
private $components = null;
/** @var Skin */
private $skin;
/**
* @param Skin $skin
*/
public function __construct( Skin $skin ) {
$this->skin = $skin;
}
/**
* Get a component. This method has side effects in that
* if registered components have been not initialized they
* will be registered as part of this method.
*
* @param string $name
* @throws RuntimeException with unknown name
* @return SkinComponent
*/
public function getComponent( string $name ): SkinComponent {
if ( $this->components === null ) {
$this->registerComponents();
}
$component = $this->components[$name] ?? null;
if ( !$component ) {
throw new RuntimeException( 'Unknown component: ' . $name );
}
return $component;
}
/**
* Return all registered components.
*
* @since 1.38
* @return SkinComponent[]
*/
public function getComponents() {
if ( $this->components === null ) {
$this->registerComponents();
}
return $this->components;
}
/**
* Registers a component for use with the skin.
* Private for now, but in future we may consider making this a
* public method to allow skins to extend component definitions.
*
* @param string $name
* @throws RuntimeException if given an unknown name
*/
private function registerComponent( string $name ) {
switch ( $name ) {
case 'logos':
$component = new SkinComponentLogo(
$this->skin->getConfig(),
$this->skin->getOutput()->getTitle()
);
break;
default:
throw new RuntimeException( 'Unknown component: ' . $name );
}
$this->components[$name] = $component;
}
/**
* Registers components used by skin.
*/
private function registerComponents() {
$this->registerComponent( 'logos' );
}
}

View file

@ -87,7 +87,7 @@ class SkinMustacheTest extends MediaWikiIntegrationTestCase {
/**
* @covers SkinTemplate::getTemplateData
* @covers SkinTemplate::buildSearchProps
* @covers MediaWiki\Skin\SkinComponentLogo::getTemplateData
*/
public function testGetTemplateData() {
$config = $this->getServiceContainer()->getMainConfig();