includes/MobileContext.php
<?php
use MediaWiki\Config\Config;
use MediaWiki\Context\ContextSource;
use MediaWiki\Context\IContextSource;
use MediaWiki\Context\RequestContext;
use MediaWiki\MediaWikiServices;
use MediaWiki\Utils\UrlUtils;
use MobileFrontend\Devices\DeviceDetectorService;
use MobileFrontend\WMFBaseDomainExtractor;
/**
* Provide various request-dependant methods to use in mobile context
*/
class MobileContext extends ContextSource {
public const MODE_BETA = 'beta';
public const MODE_STABLE = 'stable';
public const OPTIN_COOKIE_NAME = 'optin';
public const STOP_MOBILE_REDIRECT_COOKIE_NAME = 'stopMobileRedirect';
public const USEFORMAT_COOKIE_NAME = 'mf_useformat';
public const USER_MODE_PREFERENCE_NAME = 'mfMode';
// Keep in sync with https://wikitech.wikimedia.org/wiki/X-Analytics.
private const ANALYTICS_HEADER_KEY = 'mf-m';
private const ANALYTICS_HEADER_DELIMITER = ',';
private const ANALYTICS_HEADER_VALUE_BETA = 'b';
private const ANALYTICS_HEADER_VALUE_AMC = 'amc';
/**
* Saves the testing mode user has opted in: 'beta' or 'stable'
* @var string|null
*/
protected $mobileMode = null;
/**
* Save explicitly requested format
* @var string|null
*/
protected $useFormat = null;
/**
* Key/value pairs of things to add to X-Analytics response header for analytics
* @var array[]
*/
protected $analyticsLogItems = [];
/**
* The memoized result of `MobileContext#isMobileDevice`.
*
* This defaults to `null`, meaning that `MobileContext#isMobileDevice` has
* yet to be called.
*
* @see MobileContext#isMobileDevice
*
* @var bool|null
*/
private $isMobileDevice = null;
/**
* Saves requested Mobile action
* @var string|null
*/
protected $mobileAction = null;
/**
* Save whether mobile view is explicitly requested
* @var bool
*/
private $forceMobileView = false;
/**
* Save whether or not we should display the mobile view
* @var bool|null
*/
private $mobileView = null;
/**
* Have we already checked for desktop/mobile view toggling?
* @var bool
*/
private $toggleViewChecked = false;
/**
* @var self|null
*/
private static $instance = null;
/**
* @var string|null What to switch the view to
*/
private $viewChange = null;
/**
* @var string|null Domain to use for the stopMobileRedirect cookie
*/
public static $mfStopRedirectCookieHost = null;
/**
* In-process cache for checking whether the current wiki has a mobile URL that's
* different from the desktop one.
* @var bool|null
*/
private $hasMobileUrl = null;
/**
* @var Config
*/
private $config;
/**
* Returns the actual MobileContext Instance or create a new if no exists
* @deprecated use MediaWikiServices::getInstance()->getService( 'MobileFrontend.Context' );
* @return self
*/
public static function singleton() {
if ( !self::$instance ) {
self::$instance = new self(
RequestContext::getMain(),
MediaWikiServices::getInstance()->getService( 'MobileFrontend.Config' )
);
}
return self::$instance;
}
/**
* Resets the singleton instance.
*/
public static function resetInstanceForTesting() {
self::$instance = null;
}
/**
* @param IContextSource $context
* @param Config $config
*/
protected function __construct( IContextSource $context, Config $config ) {
$this->setContext( $context );
$this->config = $config;
}
/**
* Detects whether the UA is sending the request from a device and, if so,
* whether to display the mobile view to that device.
*
* The mobile view will always be displayed to mobile devices. However, it
* will only be displayed to tablet devices if `$wgMFShowMobileViewToTablets`
* is truthy.
*
* @fixme This should be renamed to something more appropriate, e.g.
* `shouldDisplayMobileViewToDevice`.
*
* @see MobileContext::shouldDisplayMobileView
*
* @return bool
*/
public function isMobileDevice() {
if ( $this->isMobileDevice !== null ) {
return $this->isMobileDevice;
}
$this->isMobileDevice = false;
$properties = DeviceDetectorService::factory( $this->config )
->detectDeviceProperties( $this->getRequest(), $_SERVER );
if ( $properties ) {
$showMobileViewToTablets = $this->config->get( 'MFShowMobileViewToTablets' );
$this->isMobileDevice =
$properties->isMobileDevice()
|| ( $properties->isTabletDevice() && $showMobileViewToTablets );
}
return $this->isMobileDevice;
}
/**
* Save whether mobile view should always be enforced
* @param bool $value should mobile view be enforced?
*/
public function setForceMobileView( $value ) {
$this->forceMobileView = $value;
}
/**
* Sets the value of $this->mobileMode property to the value of the 'optin' cookie.
* If the cookie is not set the value will be an empty string.
*/
private function loadMobileModeCookie() {
$this->mobileMode = $this->getRequest()->getCookie( self::OPTIN_COOKIE_NAME, '' );
}
/**
* Returns the testing mode user has opted in: 'beta' or any other value for stable
* @return string
*/
private function getMobileMode() {
$enableBeta = $this->config->get( 'MFEnableBeta' );
if ( !$enableBeta ) {
return '';
}
if ( $this->mobileMode === null ) {
$mobileAction = $this->getMobileAction();
if ( $mobileAction === self::MODE_BETA || $mobileAction === self::MODE_STABLE ) {
$this->mobileMode = $mobileAction;
} else {
$user = $this->getUser();
if ( !$user->isRegistered() ) {
$this->loadMobileModeCookie();
} else {
$userOptionManager = MediaWikiServices::getInstance()->getUserOptionsManager();
$mode = $userOptionManager->getOption( $user, self::USER_MODE_PREFERENCE_NAME );
$this->mobileMode = $mode;
// Edge case where preferences are corrupt or the user opted
// in before change.
if ( $mode === null ) {
// Should we set the user option here?
$this->loadMobileModeCookie();
}
}
}
}
return $this->mobileMode;
}
/**
* Sets testing group membership, both cookie and this class variables
*
* WARNING: Does not persist the updated user preference to the database.
* The caller must handle this by calling User::saveSettings() after all
* preference updates associated with this web request are made.
*
* @param string $mode Mode to set
*/
public function setMobileMode( $mode ) {
if ( $mode !== self::MODE_BETA ) {
$mode = '';
}
$services = MediaWikiServices::getInstance();
$this->mobileMode = $mode;
$user = $this->getUser();
if ( $user->getId() ) {
$userOptionsManager = $services->getUserOptionsManager();
$userOptionsManager->setOption(
$user,
self::USER_MODE_PREFERENCE_NAME,
$mode
);
}
$this->getRequest()->response()->setCookie( self::OPTIN_COOKIE_NAME, $mode, 0, [
'prefix' => '',
'domain' => $this->getCookieDomain()
] );
}
/**
* Whether user is Beta group member
* @return bool
*/
public function isBetaGroupMember() {
return $this->getMobileMode() === self::MODE_BETA;
}
/**
* Whether the current user is has advanced mobile contributions enabled.
* @return bool
*/
private static function isAmcUser() {
$services = MediaWikiServices::getInstance();
/** @var \MobileFrontend\Amc\UserMode $userMode */
$userMode = $services->getService( 'MobileFrontend.AMC.UserMode' );
return $userMode->isEnabled();
}
/**
* Determine whether or not we should display the mobile view
*
* Step through the hierarchy of what should or should not trigger
* the mobile view.
*
* Primacy is given to the page action - we will never show mobile view
* for page edits or page history. 'userformat' request param is then
* honored, followed by cookie settings, then actual device detection,
* finally falling back on false.
* @return bool
*/
public function shouldDisplayMobileView() {
if ( $this->mobileView !== null ) {
return $this->mobileView;
}
// check if we need to toggle between mobile/desktop view
$this->checkToggleView();
$this->mobileView = $this->shouldDisplayMobileViewInternal();
return $this->mobileView;
}
/**
* Value for shouldDisplayMobileView()
* @return bool
*/
private function shouldDisplayMobileViewInternal() {
// May be overridden programmatically
if ( $this->forceMobileView ) {
return true;
}
// always display desktop or mobile view if it's explicitly requested
$useFormat = $this->getUseFormat();
if ( $useFormat == 'desktop' ) {
return false;
} elseif ( $useFormat == 'mobile' ) {
return true;
}
if ( $this->getRequest()->getRawVal( 'mobileformat' ) !== null ) {
return true;
}
/**
* If a user is accessing the site from a mobile domain, then we should
* always display the mobile version of the site (otherwise, the cache
* may get polluted). See
* https://phabricator.wikimedia.org/T48473
*/
if ( $this->usingMobileDomain() ) {
return true;
}
// check cookies for what to display
$useMobileFormat = $this->getUseFormatCookie();
if ( $useMobileFormat == 'true' ) {
return true;
}
$stopMobileRedirect = $this->getStopMobileRedirectCookie();
if ( $stopMobileRedirect == 'true' ) {
return false;
}
// do device detection
if ( $this->isMobileDevice() ) {
return true;
}
return false;
}
/**
* Get requested mobile action
* @return string
*/
public function getMobileAction() {
if ( $this->mobileAction === null ) {
$this->mobileAction = $this->getRequest()->getRawVal( 'mobileaction' );
}
return $this->mobileAction;
}
/**
* Gets the value of the `useformat` query string parameter.
*
* @return string Typically "desktop" or "mobile"
*/
private function getUseFormat() {
if ( $this->useFormat === null ) {
$this->useFormat = $this->getRequest()->getRawVal( 'useformat' );
}
return $this->useFormat;
}
/**
* Set Cookie to stop automatically redirect to mobile page
* @param int|null $expiry Expire time of cookie
*/
public function setStopMobileRedirectCookie( $expiry = null ) {
$stopMobileRedirectCookieSecureValue =
$this->config->get( 'MFStopMobileRedirectCookieSecureValue' );
$this->getRequest()->response()->setCookie(
self::STOP_MOBILE_REDIRECT_COOKIE_NAME,
'true',
$expiry ?? $this->getUseFormatCookieExpiry(),
[
'domain' => $this->getStopMobileRedirectCookieDomain(),
'prefix' => '',
'secure' => (bool)$stopMobileRedirectCookieSecureValue,
]
);
}
/**
* Remove cookie and continue automatic redirect to mobile page
*/
public function unsetStopMobileRedirectCookie() {
if ( $this->getStopMobileRedirectCookie() === null ) {
return;
}
$expire = $this->getUseFormatCookieExpiry( time(), -3600 );
$this->setStopMobileRedirectCookie( $expire );
}
/**
* Read cookie for stop automatic mobile redirect
* @return string
*/
public function getStopMobileRedirectCookie() {
$stopMobileRedirectCookie = $this->getRequest()
->getCookie( self::STOP_MOBILE_REDIRECT_COOKIE_NAME, '' );
return $stopMobileRedirectCookie;
}
/**
* This cookie can determine whether or not a user should see the mobile
* version of a page.
*
* @return string|null
*/
public function getUseFormatCookie() {
$useFormatFromCookie = $this->getRequest()->getCookie( self::USEFORMAT_COOKIE_NAME, '' );
return $useFormatFromCookie;
}
/**
* Return the base level domain or IP address
*
* @return string|null
*/
public function getCookieDomain() {
$helper = new WMFBaseDomainExtractor();
return $helper->getCookieDomain( $this->config->get( 'Server' ) );
}
/**
* Determine the correct domain to use for the stopMobileRedirect cookie
*
* Will use $wgMFStopRedirectCookieHost if it's set, otherwise will use
* result of getCookieDomain()
* @return string|null
*/
public function getStopMobileRedirectCookieDomain() {
$mfStopRedirectCookieHost = $this->config->get( 'MFStopRedirectCookieHost' );
if ( !$mfStopRedirectCookieHost ) {
self::$mfStopRedirectCookieHost = $this->getCookieDomain();
} else {
self::$mfStopRedirectCookieHost = $mfStopRedirectCookieHost;
}
return self::$mfStopRedirectCookieHost;
}
/**
* Set the mf_useformat cookie
*
* This cookie can determine whether or not a user should see the mobile
* version of pages.
*
* @param string $cookieFormat should user see mobile version of pages?
* @param int|null $expiry Expiration of cookie
*/
public function setUseFormatCookie( $cookieFormat = 'true', $expiry = null ) {
$this->getRequest()->response()->setCookie(
self::USEFORMAT_COOKIE_NAME,
$cookieFormat,
$expiry ?? $this->getUseFormatCookieExpiry(),
[
'prefix' => '',
'httpOnly' => true,
]
);
$stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
$stats->updateCount( 'mobile.useformat_' . $cookieFormat . '_cookie_set', 1 );
}
/**
* Remove cookie based saved useformat value
*/
public function unsetUseFormatCookie() {
if ( $this->getUseFormatCookie() === null ) {
return;
}
// set expiration date in the past
$expire = $this->getUseFormatCookieExpiry( time(), -3600 );
$this->setUseFormatCookie( '', $expire );
}
/**
* Get the expiration time for the mf_useformat cookie
*
* @param int|null $startTime The base time (in seconds since Epoch) from which to calculate
* cookie expiration. If null, time() is used.
* @param int|null $cookieDuration The time (in seconds) the cookie should last
* @return int The time (in seconds since Epoch) that the cookie should expire
*/
protected function getUseFormatCookieExpiry( $startTime = null, $cookieDuration = null ) {
// use $cookieDuration if it's valid
if ( intval( $cookieDuration ) === 0 ) {
$cookieDuration = $this->getUseFormatCookieDuration();
}
// use $startTime if it's valid
if ( intval( $startTime ) === 0 ) {
$startTime = time();
}
$expiry = $startTime + $cookieDuration;
return $expiry;
}
/**
* Determine the duration the cookie should last.
*
* If $wgMobileFrontendFormatcookieExpiry has a non-0 value, use that
* for the duration. Otherwise, fall back to $wgCookieExpiration.
*
* @return int The number of seconds for which the cookie should last.
*/
public function getUseFormatCookieDuration() {
$mobileFrontendFormatCookieExpiry =
$this->config->get( 'MobileFrontendFormatCookieExpiry' );
$cookieExpiration = $this->getConfig()->get( 'CookieExpiration' );
$cookieDuration = ( abs( intval( $mobileFrontendFormatCookieExpiry ) ) > 0 ) ?
$mobileFrontendFormatCookieExpiry : $cookieExpiration;
return $cookieDuration;
}
/**
* Returns the callback from $wgMobileUrlCallback, which changes
* a desktop domain into a mobile domain.
* @return callable|null
* @phan-return callable(string):string|null
*/
private function getMobileUrlCallback(): ?callable {
return $this->config->get( 'MobileUrlCallback' );
}
/**
* True if the current wiki has separate mobile and desktop domains (regardless
* of which domain is used by the current request).
* @return bool
*/
public function hasMobileDomain(): bool {
if ( $this->hasMobileUrl === null ) {
$mobileUrlCallback = $this->getMobileUrlCallback();
if ( $mobileUrlCallback ) {
$urlUtils = MediaWikiServices::getInstance()->getUrlUtils();
$server = $urlUtils->expand( $this->getConfig()->get( 'Server' ), PROTO_CANONICAL ) ?? '';
$host = $urlUtils->parse( $server )['host'] ?? '';
$mobileDomain = call_user_func( $mobileUrlCallback, $host );
$this->hasMobileUrl = $mobileDomain !== $host;
} else {
$this->hasMobileUrl = false;
}
}
return $this->hasMobileUrl;
}
/**
* Take a URL and return the equivalent mobile URL (ie. replace the domain with the
* mobile domain).
*
* Typically this is a URL for the current wiki, but it can be anything as long as
* $wgMobileUrlCallback can convert its domain (so e.g. interwiki links can be
* converted). If the domain is already a mobile domain, or not recognized by
* $wgMobileUrlCallback, or the wiki does not use mobile domains and so
* $wgMobileUrlCallback is not set, the URL will be returned unchanged (except
* $forceHttps will still be applied).
*
* @param string $url URL to convert
* @param bool $forceHttps Force HTTPS, even if the original URL used HTTP
* @return string|bool
*/
public function getMobileUrl( $url, $forceHttps = false ) {
$urlUtils = MediaWikiServices::getInstance()->getUrlUtils();
$parsedUrl = $urlUtils->parse( $url );
// if parsing failed, maybe it's a local Url, try to expand and reparse it - task T107505
if ( !$parsedUrl ) {
$expandedUrl = $urlUtils->expand( $url, PROTO_CURRENT );
if ( $expandedUrl ) {
$parsedUrl = $urlUtils->parse( $expandedUrl );
}
if ( !$expandedUrl || !$parsedUrl ) {
return false;
}
}
$mobileUrlCallback = $this->getMobileUrlCallback();
if ( $mobileUrlCallback ) {
$parsedUrl['host'] = call_user_func( $mobileUrlCallback, $parsedUrl['host'] );
}
if ( $forceHttps ) {
$parsedUrl['scheme'] = 'https';
$parsedUrl['delimiter'] = '://';
}
$assembleUrl = UrlUtils::assemble( $parsedUrl );
return $assembleUrl;
}
/**
* Checks whether the current request is using the mobile domain.
*
* This assumes that some infrastructure outside MediaWiki will set a
* header (specified by $wgMFMobileHeader) on requests which use the
* mobile domain. This means that the traffic routing layer can rewrite
* hostnames to be canonical, so non-MobileFrontend-aware code can still
* work.
*
* @return bool
*/
public function usingMobileDomain() {
$mobileHeader = $this->config->get( 'MFMobileHeader' );
return ( $this->hasMobileDomain()
&& $mobileHeader
&& $this->getRequest()->getHeader( $mobileHeader ) !== false
);
}
/**
* Take a URL and return a copy that removes any mobile tokens.
*
* This only works with URLs of the current wiki.
*
* @param string $url representing a page on the mobile domain e.g. `https://en.m.wikipedia.org/`
* @return string (absolute url)
*/
public function getDesktopUrl( $url ) {
$urlUtils = MediaWikiServices::getInstance()->getUrlUtils();
$parsedUrl = $urlUtils->parse( $url ) ?? [];
$this->updateDesktopUrlHost( $parsedUrl );
$this->updateDesktopUrlQuery( $parsedUrl );
$desktopUrl = UrlUtils::assemble( $parsedUrl );
return $desktopUrl;
}
/**
* Update the host of a given URL to strip out any mobile tokens
* @param array &$parsedUrl Result of parseUrl() or UrlUtils::parse()
*/
protected function updateDesktopUrlHost( array &$parsedUrl ) {
$server = $this->getConfig()->get( 'Server' );
if ( !$this->hasMobileDomain() ) {
return;
}
$urlUtils = MediaWikiServices::getInstance()->getUrlUtils();
$parsedWgServer = $urlUtils->parse( $server );
$parsedUrl['host'] = $parsedWgServer['host'] ?? '';
}
/**
* Update the query portion of a given URL to remove any 'useformat' params
* @param array &$parsedUrl Result of parseUrl() or UrlUtils::parse()
*/
protected function updateDesktopUrlQuery( array &$parsedUrl ) {
if ( isset( $parsedUrl['query'] ) && strpos( $parsedUrl['query'], 'useformat' ) !== false ) {
$query = wfCgiToArray( $parsedUrl['query'] );
unset( $query['useformat'] );
$parsedUrl['query'] = wfArrayToCgi( $query );
}
}
/**
* Toggles view to one specified by the user
*
* If a user has requested a particular view (eg clicked 'Desktop' from
* a mobile page), set the requested view for this particular request
* and set a cookie to keep them on that view for subsequent requests.
*
* @param string $view User requested particular view
*/
public function toggleView( $view ) {
$this->viewChange = $view;
if ( !$this->hasMobileDomain() ) {
$this->useFormat = $view;
}
}
/**
* Performs view change as requested vy toggleView()
*/
public function doToggling() {
// make sure viewChange is set
$this->shouldDisplayMobileView();
if ( !$this->viewChange ) {
return;
}
$title = $this->getTitle();
if ( !$title ) {
return;
}
$query = $this->getRequest()->getQueryValues();
unset( $query['mobileaction'] );
unset( $query['useformat'] );
unset( $query['title'] );
$url = $title->getFullURL( $query, false, PROTO_CURRENT );
if ( $this->viewChange == 'mobile' ) {
// unset stopMobileRedirect cookie
// @TODO is this necessary with unsetting the cookie via JS?
$this->unsetStopMobileRedirectCookie();
// if no mobile domain support, set mobile cookie
if ( !$this->hasMobileDomain() ) {
$this->setUseFormatCookie();
} else {
// else redirect to mobile domain
$mobileUrl = $this->getMobileUrl( $url );
$this->getOutput()->redirect( $mobileUrl, 301 );
}
} elseif ( $this->viewChange == 'desktop' ) {
// set stopMobileRedirect cookie
$this->setStopMobileRedirectCookie();
// unset useformat cookie
if ( $this->getUseFormatCookie() == "true" ) {
$this->unsetUseFormatCookie();
}
if ( $this->hasMobileDomain() ) {
// if there is mobile domain support, redirect to desktop domain
$desktopUrl = $this->getDesktopUrl( $url );
$this->getOutput()->redirect( $desktopUrl, 301 );
}
}
}
/**
* Determine whether or not we need to toggle the view, and toggle it
*/
public function checkToggleView() {
if ( !$this->toggleViewChecked ) {
$this->toggleViewChecked = true;
$mobileAction = $this->getMobileAction();
if ( $mobileAction == 'toggle_view_desktop' ) {
$this->toggleView( 'desktop' );
} elseif ( $mobileAction == 'toggle_view_mobile' ) {
$this->toggleView( 'mobile' );
}
}
}
/**
* Determine whether or not a given URL is local
*
* @param string $url URL to check against
* @return bool
*/
public function isLocalUrl( $url ) {
$urlUtils = MediaWikiServices::getInstance()->getUrlUtils();
$parsedTargetHost = $urlUtils->parse( $url )['host'] ?? '';
$parsedServerHost = $urlUtils->parse( $this->config->get( 'Server' ) )['host'] ?? '';
return $parsedTargetHost === $parsedServerHost;
}
/**
* Add key/value pairs for analytics purposes to $this->analyticsLogItems. Pre-existing entries
* are appended to as sets delimited by commas.
* @param string $key for <key> in `X-Analytics: <key>=<value>`
* @param string $val for <value> in `X-Analytics: <key>=<value>`
*/
public function addAnalyticsLogItem( $key, $val ) {
$key = trim( $key );
$val = trim( $val );
$items = $this->analyticsLogItems[$key] ?? [];
if ( !in_array( $val, $items ) ) {
$items[] = $val;
$this->analyticsLogItems[$key] = $items;
}
}
/**
* Read key/value pairs for analytics purposes from $this->analyticsLogItems
* @return array
*/
public function getAnalyticsLogItems() {
return array_map(
static function ( $val ) {
return implode( self::ANALYTICS_HEADER_DELIMITER, $val );
},
$this->analyticsLogItems
);
}
/**
* Get HTTP header string for X-Analytics
*
* This is made up of key/value pairs and is used for analytics purposes.
*
* @return string|bool
*/
public function getXAnalyticsHeader() {
$response = $this->getRequest()->response();
$currentHeader = method_exists( $response, 'getHeader' ) ?
(string)$response->getHeader( 'X-Analytics' ) : '';
parse_str( preg_replace( '/; */', '&', $currentHeader ), $logItems );
$logItems += $this->getAnalyticsLogItems();
if ( count( $logItems ) ) {
$xanalytics_items = [];
foreach ( $logItems as $key => $val ) {
$xanalytics_items[] = urlencode( $key ) . "=" . urlencode( $val );
}
$headerValue = implode( ';', $xanalytics_items );
return "X-Analytics: $headerValue";
} else {
return false;
}
}
/**
* Take a key/val pair in string format and add it to $this->analyticsLogItems
*
* @param string $xanalytics_item In the format key=value
*/
public function addAnalyticsLogItemFromXAnalytics( $xanalytics_item ) {
[ $key, $val ] = explode( '=', $xanalytics_item, 2 );
$this->addAnalyticsLogItem( urldecode( $key ), urldecode( $val ) );
}
/**
* Adds analytics log items depending on which modes are enabled for the user
*
* Invoked from MobileFrontendHooks::onRequestContextCreateSkin()
*
* Making changes to what this method logs? Make sure you update the
* documentation for the X-Analytics header: https://wikitech.wikimedia.org/wiki/X-Analytics
*/
public function logMobileMode() {
if ( $this->isBetaGroupMember() ) {
$this->addAnalyticsLogItem( self::ANALYTICS_HEADER_KEY, self::ANALYTICS_HEADER_VALUE_BETA );
}
if ( self::isAmcUser() ) {
$this->addAnalyticsLogItem( self::ANALYTICS_HEADER_KEY, self::ANALYTICS_HEADER_VALUE_AMC );
}
}
/**
* Gets whether Wikibase descriptions should be shown in search results,
* and watchlists; or as taglines on article pages.
* Doesn't take into account whether the wikidata descriptions
* feature has been enabled.
*
* @param string $feature which description to show?
* @param Config $config
* @return bool
* @throws DomainException If `feature` isn't one that shows Wikidata descriptions. See the
* `wgMFDisplayWikibaseDescriptions` configuration variable for detail
*/
public function shouldShowWikibaseDescriptions( $feature, Config $config ) {
$displayWikibaseDescriptions = $config->get( 'MFDisplayWikibaseDescriptions' );
if ( !isset( $displayWikibaseDescriptions[$feature] ) ) {
throw new DomainException(
"\"{$feature}\" isn't a feature that shows Wikidata descriptions."
);
}
return $displayWikibaseDescriptions[$feature];
}
}