Page MenuHomeWickedGov Phorge

CentralNoticeHooks.php
No OneTemporary

Size
24 KB
Referenced Files
None
Subscribers
None

CentralNoticeHooks.php

<?php
// phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName
use MediaWiki\ChangeTags\Hook\ChangeTagsListActiveHook;
use MediaWiki\ChangeTags\Hook\ListDefinedTagsHook;
use MediaWiki\Hook\CanonicalNamespacesHook;
use MediaWiki\Hook\PreferencesGetIconHook;
use MediaWiki\Hook\SkinTemplateNavigation__UniversalHook;
use MediaWiki\MediaWikiServices;
use MediaWiki\Message\Message;
use MediaWiki\Output\OutputPage;
use MediaWiki\Preferences\Hook\GetPreferencesHook;
use MediaWiki\Registration\ExtensionRegistry;
use MediaWiki\ResourceLoader as RL;
use MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook;
use MediaWiki\ResourceLoader\ResourceLoader;
use MediaWiki\Skin\Skin;
use MediaWiki\SpecialPage\SpecialPage;
use MediaWiki\User\User;
/**
* General hook definitions
*
* This file is part of the CentralNotice Extension to MediaWiki
* https://www.mediawiki.org/wiki/Extension:CentralNotice
*
* @file
* @ingroup Extensions
*
* @section LICENSE
* 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
*/
class CentralNoticeHooks implements
CanonicalNamespacesHook,
ChangeTagsListActiveHook,
ListDefinedTagsHook,
SkinTemplateNavigation__UniversalHook,
ResourceLoaderRegisterModulesHook,
GetPreferencesHook,
PreferencesGetIconHook
{
/**
* Conditional configuration
*/
public static function onRegistration() {
// @phpcs:ignore MediaWiki.Usage.DeprecatedGlobalVariables.Deprecated$wgHooks
global $wgHooks, $wgNoticeInfrastructure, $wgSpecialPages,
$wgCentralNoticeLoader, $wgNoticeUseTranslateExtension,
$wgAvailableRights, $wgGroupPermissions, $wgCentralNoticeAdminGroup,
$wgCentralNoticeMessageProtectRight, $wgResourceModules,
$wgDefaultUserOptions;
// If CentralNotice banners should be shown on this wiki, load the components we need for
// showing banners. For discussion of banner loading strategies, see
// http://wikitech.wikimedia.org/view/CentralNotice/Optimizing_banner_loading
if ( $wgCentralNoticeLoader ) {
$wgHooks['MakeGlobalVariablesScript'][] =
'CentralNoticeHooks::onMakeGlobalVariablesScript';
$wgHooks['BeforePageDisplay'][] = 'CentralNoticeHooks::onBeforePageDisplay';
$wgHooks['SiteNoticeAfter'][] = 'CentralNoticeHooks::onSiteNoticeAfter';
$wgHooks['ResourceLoaderGetConfigVars'][] =
'CentralNoticeHooks::onResourceLoaderGetConfigVars';
}
// Set default user preferences for campaign type filtering.
// All types are on by default.
foreach ( CampaignType::getTypes() as $type ) {
$wgDefaultUserOptions[ $type->getPreferenceKey() ] = 1;
}
// If this is the wiki that hosts the management interface, load further components
if ( $wgNoticeInfrastructure ) {
if ( $wgNoticeUseTranslateExtension ) {
$wgHooks['TranslatePostInitGroups'][] = 'BannerMessageGroup::registerGroupHook';
$wgHooks['TranslateEventMessageGroupStateChange'][] =
'BannerMessageGroup::updateBannerGroupStateHook';
}
$wgSpecialPages['CentralNotice'] = CentralNotice::class;
$wgSpecialPages['NoticeTemplate'] = SpecialNoticeTemplate::class;
$wgSpecialPages['BannerAllocation'] = SpecialBannerAllocation::class;
$wgSpecialPages['CentralNoticeLogs'] = SpecialCentralNoticeLogs::class;
$wgSpecialPages['CentralNoticeBanners'] = SpecialCentralNoticeBanners::class;
$moduleTemplate = [
'localBasePath' => dirname( __DIR__ ) . '/resources',
'remoteExtPath' => 'CentralNotice/resources',
];
$wgResourceModules += [
'ext.centralNotice.adminUi' => $moduleTemplate + [
'dependencies' => [
'jquery.ui',
'mediawiki.jqueryMsg',
'mediawiki.util',
],
'scripts' => [
'vendor/jquery.ui.multiselect/ui.multiselect.js',
'vendor/jquery.jstree/jstree.js',
'infrastructure/centralnotice.js',
],
'styles' => [
'vendor/jquery.ui.multiselect/ui.multiselect.css',
'vendor/jquery.jstree/themes/default/style.css',
'infrastructure/ext.centralNotice.adminUi.less'
],
'messages' => [
'centralnotice-documentwrite-error',
'centralnotice-close-title',
'centralnotice-select-all',
'centralnotice-remove-all',
'centralnotice-items-selected',
'centralnotice-geo-status'
]
],
'ext.centralNotice.adminUi.campaignPager' => $moduleTemplate + [
'scripts' => 'infrastructure/ext.centralNotice.adminUi.campaignPager.js',
'styles' => 'infrastructure/ext.centralNotice.adminUi.campaignPager.less'
],
'ext.centralNotice.adminUi.bannerManager' => $moduleTemplate + [
'dependencies' => [
'ext.centralNotice.adminUi',
'jquery.ui',
],
'scripts' => 'infrastructure/bannermanager.js',
'styles' => 'infrastructure/bannermanager.less',
'messages' => [
'centralnotice-add-notice-button',
'centralnotice-add-notice-cancel-button',
'centralnotice-archive-banner',
'centralnotice-archive-banner-title',
'centralnotice-archive-banner-confirm',
'centralnotice-archive-banner-cancel',
'centralnotice-add-new-banner-title',
'centralnotice-delete-banner',
'centralnotice-delete-banner-title',
'centralnotice-delete-banner-confirm',
'centralnotice-delete-banner-cancel'
]
],
'ext.centralNotice.adminUi.bannerEditor' => $moduleTemplate + [
'dependencies' => [
'ext.centralNotice.adminUi',
'jquery.ui',
'ext.centralNotice.kvStore',
'mediawiki.api',
'mediawiki.Title',
'mediawiki.user',
],
'scripts' => 'infrastructure/bannereditor.js',
'styles' => 'infrastructure/bannereditor.css',
'messages' => [
'centralnotice-clone',
'centralnotice-clone-notice',
'centralnotice-clone-cancel',
'centralnotice-archive-banner',
'centralnotice-archive-banner-title',
'centralnotice-archive-banner-confirm',
'centralnotice-archive-banner-cancel',
'centralnotice-delete-banner',
'centralnotice-delete-banner-title',
'centralnotice-delete-banner-confirm',
'centralnotice-delete-banner-cancel',
'centralnotice-banner-cdn-dialog-waiting-text',
'centralnotice-banner-cdn-dialog-error',
'centralnotice-banner-cdn-dialog-success',
'centralnotice-fieldset-preview',
'centralnotice-preview-page',
'centralnotice-update-preview',
'centralnotice-preview-loader-error-dialog-title',
]
],
'ext.centralNotice.adminUi.campaignManager' => [
'localBasePath' => dirname( __DIR__ ),
'remoteExtPath' => 'CentralNotice',
'dependencies' => [
'ext.centralNotice.adminUi',
'oojs-ui',
'mediawiki.template',
'mediawiki.template.mustache'
],
'scripts' => 'resources/infrastructure/campaignManager.js',
'styles' => 'resources/infrastructure/campaignManager.less',
'templates' => [
'campaignMixinParamControls.mustache' => 'templates/campaignMixinParamControls.mustache'
],
'messages' => [
'centralnotice-notice-mixins-int-required',
'centralnotice-notice-mixins-float-required',
'centralnotice-notice-mixins-out-of-bound',
'centralnotice-banner-history-logger-rate',
'centralnotice-banner-history-logger-rate-help',
'centralnotice-banner-history-logger-max-entry-age',
'centralnotice-banner-history-logger-max-entry-age-help',
'centralnotice-banner-history-logger-max-entries',
'centralnotice-banner-history-logger-max-entries-help',
'centralnotice-banner-history-logger-wait-log-no-send-beacon',
'centralnotice-banner-history-logger-wait-log-no-send-beacon-help',
'centralnotice-set-record-impression-sample-rate',
'centralnotice-custom-record-impression-sample-rate',
'centralnotice-banners-not-guaranteed-to-display',
'centralnotice-impression-diet-identifier',
'centralnotice-impression-diet-identifier-help',
'centralnotice-impression-diet-maximum-seen',
'centralnotice-impression-diet-maximum-seen-help',
'centralnotice-impression-diet-restart-cycle-delay',
'centralnotice-impression-diet-restart-cycle-delay-help',
'centralnotice-impression-diet-skip-initial',
'centralnotice-impression-diet-skip-initial-help',
'centralnotice-large-banner-limit-days',
'centralnotice-large-banner-limit-days-help',
'centralnotice-large-banner-limit-randomize',
'centralnotice-large-banner-limit-randomize-help',
'centralnotice-large-banner-limit-identifier',
'centralnotice-large-banner-limit-identifier-help',
'centralnotice-impression-events-sample-rate',
'centralnotice-impression-events-sample-rate-help',
'centralnotice-impression-events-sample-rate-field'
]
],
'ext.centralNotice.adminUi.bannerSequence' => $moduleTemplate + [
'scripts' => 'infrastructure/ext.centralNotice.adminUi.bannerSequence.js',
'styles' => 'infrastructure/ext.centralNotice.adminUi.bannerSequence.less',
'dependencies' => [
'ext.centralNotice.adminUi.campaignManager',
'oojs-ui',
'oojs-ui.styles.icons-moderation',
'mediawiki.jqueryMsg'
],
'messages' => [
'centralnotice-banner-sequence',
'centralnotice-banner-sequence-days',
'centralnotice-banner-sequence-days-error',
'centralnotice-banner-sequence-days-help',
'centralnotice-banner-sequence-help',
'centralnotice-banner-sequence-bucket-seq',
'centralnotice-banner-sequence-bucket-add-step',
'centralnotice-banner-sequence-banner',
'centralnotice-banner-sequence-page-views',
'centralnotice-banner-sequence-skip-with-id',
'centralnotice-banner-sequence-page-views-error',
'centralnotice-banner-sequence-skip-with-id-error',
'centralnotice-banner-sequence-banner-removed-error',
'centralnotice-banner-sequence-no-banner',
'centralnotice-banner-sequence-detailed-help'
]
],
];
// Register user rights for editing
$wgAvailableRights[] = 'centralnotice-admin';
if ( $wgCentralNoticeAdminGroup ) {
// Grant admin permissions to this group
$wgGroupPermissions[$wgCentralNoticeAdminGroup]['centralnotice-admin'] = true;
}
if ( !in_array( $wgCentralNoticeMessageProtectRight, $wgAvailableRights ) ) {
$wgAvailableRights[] = $wgCentralNoticeMessageProtectRight;
}
self::addCascadingRestrictionRight( $wgCentralNoticeMessageProtectRight );
self::addCascadingRestrictionRight( 'centralnotice-admin' );
}
}
/**
* @param string $right
*/
private static function addCascadingRestrictionRight( $right ) {
global $wgCascadingRestrictionLevels, $wgRestrictionLevels;
if ( !in_array( $right, $wgRestrictionLevels ) ) {
$wgRestrictionLevels[] = $right;
}
if ( !in_array( $right, $wgCascadingRestrictionLevels ) ) {
$wgCascadingRestrictionLevels[] = $right;
}
}
/**
* Initialization: set default values for some config globals. Invoked via
* $wgExtensionFunctions.
*/
public static function initCentralNotice() {
global $wgCentralBannerRecorder, $wgCentralSelectedBannerDispatcher,
$wgCentralSelectedMobileBannerDispatcher;
// Defaults for infrastructure wiki URLs
if ( !$wgCentralBannerRecorder ) {
$wgCentralBannerRecorder =
SpecialPage::getTitleFor( 'RecordImpression' )->getLocalUrl();
}
if ( !$wgCentralSelectedBannerDispatcher ) {
$wgCentralSelectedBannerDispatcher =
SpecialPage::getTitleFor( 'BannerLoader' )->getLocalUrl();
}
if ( !$wgCentralSelectedMobileBannerDispatcher &&
ExtensionRegistry::getInstance()->isLoaded( 'MobileFrontend' )
) {
$wgCentralSelectedMobileBannerDispatcher = $wgCentralSelectedBannerDispatcher;
}
}
/**
* CanonicalNamespaces hook; adds the CentralNotice namespaces if this is an infrastructure
* wiki, and if CentralNotice is configured to use the Translate extension.
*
* We do this here because there are initialization problems wrt Translate and MW core if
* the language object is initialized before all namespaces are registered -- which would
* be the case if we just used the wgExtensionFunctions hook system.
*
* @param array &$namespaces Modifiable list of namespaces -- similar to $wgExtraNamespaces
*/
public function onCanonicalNamespaces( &$namespaces ) {
global $wgExtraNamespaces, $wgNamespacesWithSubpages, $wgTranslateMessageNamespaces;
global $wgNoticeUseTranslateExtension, $wgNoticeInfrastructure;
// TODO XXX Old doc copied from legacy follows, verify accuracy!
// When using the group review feature of translate; this
// will be the namespace ID for the banner staging area -- ie: banners
// here are world editable and will not be moved to the MW namespace
// until they are in $wgNoticeTranslateDeployStates
// TODO This may be unnecessary. Must coordinate with extension.json
if ( !defined( 'NS_CN_BANNER' ) ) {
define( 'NS_CN_BANNER', 866 );
define( 'NS_CN_BANNER_TALK', 867 );
}
if ( $wgNoticeInfrastructure && $wgNoticeUseTranslateExtension ) {
$wgExtraNamespaces[NS_CN_BANNER] = 'CNBanner';
$wgTranslateMessageNamespaces[] = NS_CN_BANNER;
$wgExtraNamespaces[NS_CN_BANNER_TALK] = 'CNBanner_talk';
$wgNamespacesWithSubpages[NS_CN_BANNER_TALK] = true;
$namespaces[NS_CN_BANNER] = 'CNBanner';
$namespaces[NS_CN_BANNER_TALK] = 'CNBanner_talk';
}
}
/**
* BeforePageDisplay hook handler
* This function adds the startUp and geoIP modules to the page as needed,
* and if there is a forced banner preview, add CSP headers and violation
* reporting javascript.
*
* @param OutputPage $out
* @param Skin $skin
* @return bool
*/
public static function onBeforePageDisplay( $out, $skin ) {
global $wgCentralHost, $wgServer, $wgCentralNoticeContentSecurityPolicy,
$wgCentralNoticeESITestString;
// Add ESI test string (see T308799)
// It is expected that only HTML comments in the form of '<!--esi ...' will be
// injected here.
// TODO Remove this once ESI tests are complete.
if ( $wgCentralNoticeESITestString ) {
$out->addHTML( $wgCentralNoticeESITestString );
}
// Always add geoIP
// TODO Separate geoIP from CentralNotice
$out->addModules( 'ext.centralNotice.geoIP' );
// Banners can contain user-contributed JavaScript.
// Do not show them when executing such scripts is generally disallowed.
$isSiteJsAllowed = $out->getAllowedModules( RL\Module::TYPE_SCRIPTS )
>= RL\Module::ORIGIN_USER_SITEWIDE;
$request = $skin->getRequest();
// If we're on a special page (or not a normal page view at all),
// editing, viewing history or a diff, bow out now
// This is to reduce the chance of bad misclicks from delayed banner loading
if (
!$isSiteJsAllowed ||
!$out->getTitle() ||
$out->getTitle()->inNamespace( NS_SPECIAL ) ||
( $request->getText( 'action' ) === 'edit' ) ||
( $request->getText( 'action' ) === 'history' ) ||
$request->getCheck( 'diff' )
) {
return true;
}
// Insert DNS prefetch for banner loading
if ( $wgCentralHost && $wgCentralHost !== $wgServer ) {
$out->addHeadItem(
'cn-dns-prefetch',
'<link rel="dns-prefetch" href="' . htmlspecialchars( $wgCentralHost ) . '" />'
);
}
// Insert the startup module
$out->addModules( 'ext.centralNotice.startUp' );
// FIXME: I80f6f469ba4c0b60 has been in core since REL1_32.
// Get rid of $wgCentralNoticeContentSecurityPolicy and use their stuff.
if (
$wgCentralNoticeContentSecurityPolicy &&
$request->getVal( 'banner' )
) {
$request->response()->header(
"content-security-policy: $wgCentralNoticeContentSecurityPolicy"
);
$out->addModules( 'ext.centralNotice.cspViolationAlert' );
}
return true;
}
/**
* MakeGlobalVariablesScript hook handler
* This function sets the pseudo-global JavaScript variables that are used by CentralNotice
*
* @param array &$vars
* @param OutputPage $out
* @return bool
*/
public static function onMakeGlobalVariablesScript( &$vars, $out ) {
global $wgNoticeProject, $wgCentralNoticeGeoIPBackgroundLookupModule;
// FIXME: Is this no longer used anywhere in JS following the switch to
// client-side banner selection? If so, remove it.
$vars[ 'wgNoticeProject' ] = $wgNoticeProject;
// No need to provide this variable if it's null, because mw.config.get()
// will return null if it's not there.
if ( $wgCentralNoticeGeoIPBackgroundLookupModule ) {
$vars[ 'wgCentralNoticeGeoIPBackgroundLookupModule' ] =
$wgCentralNoticeGeoIPBackgroundLookupModule;
}
// Output the user's registration date, total edit count, and past year's edit count.
// This is useful for banners that need to be targeted to specific types of users.
// Only do this for logged-in users, keeping anonymous user output equal (for Squid-cache).
$user = $out->getUser();
if ( $user->isRegistered() ) {
if ( $user->isBot() ) {
$userData = false;
} else {
$userData = [
// Add the user's registration date (TS_MW)
'registration' => $user->getRegistration() ?: 0
];
}
// Set the variable that will be output to the page
$vars[ 'wgNoticeUserData' ] = $userData;
}
return true;
}
/**
* SiteNoticeAfter hook handler
* This function outputs the siteNotice div that the banners are loaded into.
*
* @param string &$notice
* @return bool
*/
public static function onSiteNoticeAfter( &$notice ) {
// TODO Legacy comment below, likely inaccurate; check and fix
// Ensure that the div including #siteNotice is actually included
$notice = "<!-- CentralNotice -->$notice";
return true;
}
/**
* ResourceLoaderGetConfigVars hook handler
* Send php config vars to js via ResourceLoader
*
* @param array &$vars variables to be added to the output of the startup module
* @return bool
*/
public static function onResourceLoaderGetConfigVars( &$vars ) {
global $wgNoticeInfrastructure, $wgCentralBannerRecorder,
$wgNoticeNumberOfBuckets, $wgNoticeBucketExpiry,
$wgNoticeNumberOfControllerBuckets, $wgNoticeCookieDurations,
$wgNoticeHideUrls, $wgCentralNoticeSampleRate,
$wgCentralNoticeImpressionEventSampleRate,
$wgCentralSelectedBannerDispatcher, $wgCentralSelectedMobileBannerDispatcher,
$wgCentralNoticePerCampaignBucketExtension, $wgCentralNoticeCampaignMixins,
$wgCentralNoticeMaxCampaignFallback;
// TODO Check if the following comment still applies
// Making these calls too soon will causes issues with the namespace localisation cache.
// This seems to be just right. We require them at all because MW will 302 page requests
// made to non localised namespaces which results in wasteful extra calls.
// Set infrastructure URL variables, which change between mobile/desktop
if ( ExtensionRegistry::getInstance()->isLoaded( 'MobileFrontend' ) ) {
$mc = MobileContext::singleton();
$displayMobile = $mc->shouldDisplayMobileView();
} else {
$displayMobile = false;
}
if ( $displayMobile ) {
// @phan-suppress-next-line PhanPossiblyUndeclaredVariable
$wgCentralBannerRecorder = $mc->getMobileUrl( $wgCentralBannerRecorder );
$bannerDispatcher = $wgCentralSelectedMobileBannerDispatcher;
} else {
$bannerDispatcher = $wgCentralSelectedBannerDispatcher;
}
$vars[ 'wgCentralNoticeActiveBannerDispatcher' ] = $bannerDispatcher;
$vars[ 'wgCentralBannerRecorder' ] = $wgCentralBannerRecorder;
$vars[ 'wgCentralNoticeSampleRate' ] = $wgCentralNoticeSampleRate;
$vars[ 'wgCentralNoticeImpressionEventSampleRate' ] =
$wgCentralNoticeImpressionEventSampleRate;
$vars[ 'wgNoticeNumberOfBuckets' ] = $wgNoticeNumberOfBuckets;
$vars[ 'wgNoticeBucketExpiry' ] = $wgNoticeBucketExpiry;
$vars[ 'wgNoticeNumberOfControllerBuckets' ] = $wgNoticeNumberOfControllerBuckets;
$vars[ 'wgNoticeCookieDurations' ] = $wgNoticeCookieDurations;
$vars[ 'wgNoticeHideUrls' ] = $wgNoticeHideUrls;
$vars[ 'wgCentralNoticeMaxCampaignFallback' ] = $wgCentralNoticeMaxCampaignFallback;
$vars[ 'wgCentralNoticePerCampaignBucketExtension' ] =
$wgCentralNoticePerCampaignBucketExtension;
if ( $wgNoticeInfrastructure ) {
// Add campaign mixin defs for use in admin interface
$vars[ 'wgCentralNoticeCampaignMixins' ] = $wgCentralNoticeCampaignMixins;
}
return true;
}
/**
* Conditionally register resource loader modules.
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderRegisterModules
*
* @param ResourceLoader $resourceLoader
*/
public function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ): void {
global $wgEnableJavaScriptTest, $wgAutoloadClasses;
if ( $wgEnableJavaScriptTest ) {
// These classes are only used here or in phpunit tests
$wgAutoloadClasses['CNTestFixturesResourceLoaderModule'] =
dirname( __DIR__ ) . '/tests/phpunit/CNTestFixturesResourceLoaderModule.php';
$wgAutoloadClasses['CentralNoticeTestFixtures'] =
dirname( __DIR__ ) . '/tests/phpunit/CentralNoticeTestFixtures.php';
// Set up test fixtures module, which is added as a dependency for all QUnit
// tests.
$resourceLoader->register( 'ext.centralNotice.testFixtures', [
'class' => 'CNTestFixturesResourceLoaderModule'
] );
}
}
/**
* Add tags defined by this extension to the list of active tags.
*
* @param array &$tags List of defined or active tags
*/
public function onChangeTagsListActive( &$tags ) {
$this->addDefinedTags( $tags );
}
/**
* Add tags defined by this extension to list of defined tags.
*
* @param array &$tags List of defined or active tags
*/
public function onListDefinedTags( &$tags ) {
$this->addDefinedTags( $tags );
}
/**
* @param string[] &$tags
*/
private function addDefinedTags( &$tags ): void {
$tags[] = 'centralnotice';
$tags[] = 'centralnotice translation';
}
/**
* @param User $user
* @param array &$preferences
*/
public function onGetPreferences( $user, &$preferences ) {
// Explanatory text
$preferences['centralnotice-intro'] = [
'type' => 'info',
'default' => wfMessage( 'centralnotice-user-prefs-intro' )->parseAsBlock(),
'section' => 'centralnotice-banners',
'raw' => true,
];
foreach ( CampaignType::getTypes() as $type ) {
// This allows fallback languages while also showing something not-too-
// horrible if the config variable has types that don't have i18n
// messages.
// Note also that the value of 'label' will be escaped prior to output.
$message = Message::newFromKey( $type->getMessageKey() );
$label = $message->exists() ? $message->text() : $type->getId();
$preferences[ $type->getPreferenceKey() ] = [
'type' => 'toggle',
'section' => 'centralnotice-banners/centralnotice-display-banner-types',
'label' => $label,
'disabled' => $type->getOnForAll()
];
}
}
/**
* Add icon for Special:Preferences mobile layout
*
* @param array &$iconNames Array of icon names for their respective sections.
*/
public function onPreferencesGetIcon( &$iconNames ) {
$iconNames[ 'centralnotice-banners' ] = 'feedback';
}
/**
* Adds CentralNotice specific navigation tabs to the UI.
* Implementation of SkinTemplateNavigation::Universal hook.
*
* @param Skin $skin Reference to the Skin object
* @param array &$tabs Any current skin tabs
*/
public function onSkinTemplateNavigation__Universal( $skin, &$tabs ): void {
global $wgNoticeTabifyPages, $wgNoticeInfrastructure;
// Only show tabs if this wiki is in infrastructure mode
if ( !$wgNoticeInfrastructure ) {
return;
}
// Return if skin allows special pages to register navigation links (in which
// case this is handled by getShortDescription() and
// getAssociatedNavigationLinks()).
// See T315562, T313349.
if ( $skin->supportsMenu( 'associated-pages' ) ) {
return;
}
$title = $skin->getTitle();
// Only add tabs to special pages
if ( !$title || !$title->isSpecialPage() ) {
return;
}
[ $alias, ] = MediaWikiServices::getInstance()->getSpecialPageFactory()->
resolveAlias( $title->getText() );
if ( !array_key_exists( $alias, $wgNoticeTabifyPages ) ) {
return;
}
// Clear the special page tab that's there already
$tabs['namespaces'] = [];
// Now add our own
foreach ( $wgNoticeTabifyPages as $page => $keys ) {
$tabs[ $keys[ 'type' ] ][ $page ] = [
'text' => wfMessage( $keys[ 'message' ] )->parse(),
'href' => SpecialPage::getTitleFor( $page )->getFullURL(),
'class' => ( $alias === $page ) ? 'selected' : '',
];
}
}
}

File Metadata

Mime Type
text/x-php
Expires
Fri, Jul 3, 21:30 (11 h, 43 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
50/2f/29cc8df2b25c940e92a98c84791c
Default Alt Text
CentralNoticeHooks.php (24 KB)

Event Timeline