Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
12.07% covered (danger)
12.07%
176 / 1458
31.58% covered (danger)
31.58%
6 / 19
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hooks
12.07% covered (danger)
12.07%
176 / 1458
31.58% covered (danger)
31.58%
6 / 19
6493.39
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 factory
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 onMessageCacheFetchOverrides
83.64% covered (warning)
83.64%
138 / 165
0.00% covered (danger)
0.00%
0 / 1
18.27
 getShortRightsLink
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
20
 onSkinCopyrightFooterMessage
7.41% covered (danger)
7.41%
2 / 27
0.00% covered (danger)
0.00%
0 / 1
126.31
 onEditPageCopyrightWarning
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
72
 onDiscussionToolsTermsOfUseMessages
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
72
 onFlowTermsOfUseMessages
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
72
 onSkinAddFooterLinks
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
12
 onTorBlockBlockedMsg
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onUploadForm_initial
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 onRegistration
0.00% covered (danger)
0.00%
0 / 1087
0.00% covered (danger)
0.00%
0 / 1
20
 onOutputPageBeforeHTML
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 onResourceLoaderRegisterModules
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
6
 onSpecialPageBeforeExecute
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addIPInfoLinks
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
72
 shouldMoveDonateLink
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 onSkinTemplateNavigation__Universal
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 onSidebarBeforeOutput
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2
3namespace MediaWiki\Extension\WikimediaMessages;
4
5use HtmlArmor;
6use MediaWiki\Cache\Hook\MessageCacheFetchOverridesHook;
7use MediaWiki\Config\Config;
8use MediaWiki\Config\ConfigException;
9use MediaWiki\Config\ServiceOptions;
10use MediaWiki\Exception\ErrorPageError;
11use MediaWiki\Extension\WikimediaMessages\LogFormatter\WMLiquidThreadsLogFormatter;
12use MediaWiki\Extension\WikimediaMessages\LogFormatter\WMUserMergeLogFormatter;
13use MediaWiki\Hook\EditPageCopyrightWarningHook;
14use MediaWiki\Hook\SidebarBeforeOutputHook;
15use MediaWiki\Hook\SkinAddFooterLinksHook;
16use MediaWiki\Hook\SkinCopyrightFooterMessageHook;
17use MediaWiki\Hook\SkinTemplateNavigation__UniversalHook;
18use MediaWiki\Hook\UploadForm_initialHook;
19use MediaWiki\Html\Html;
20use MediaWiki\Linker\Linker;
21use MediaWiki\Linker\LinkRenderer;
22use MediaWiki\MainConfigNames;
23use MediaWiki\Message\Message;
24use MediaWiki\Output\Hook\OutputPageBeforeHTMLHook;
25use MediaWiki\Output\OutputPage;
26use MediaWiki\Permissions\PermissionManager;
27use MediaWiki\Registration\ExtensionRegistry;
28use MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook;
29use MediaWiki\ResourceLoader\ResourceLoader;
30use MediaWiki\Skin\Skin;
31use MediaWiki\Skin\SkinTemplate;
32use MediaWiki\SpecialPage\Hook\SpecialPageBeforeExecuteHook;
33use MediaWiki\SpecialPage\SpecialPage;
34use MediaWiki\Specials\SpecialUpload;
35use MediaWiki\Title\Title;
36use MediaWiki\User\Options\UserOptionsLookup;
37use MediaWiki\WikiMap\WikiMap;
38use MessageCache;
39use MessageLocalizer;
40use MobileContext;
41use Wikimedia\IPUtils;
42use Wikimedia\Message\MessageSpecifier;
43
44/**
45 * Hooks for WikimediaMessages extension
46 *
47 * @ingroup Extensions
48 */
49class Hooks implements
50    EditPageCopyrightWarningHook,
51    MessageCacheFetchOverridesHook,
52    OutputPageBeforeHTMLHook,
53    ResourceLoaderRegisterModulesHook,
54    SidebarBeforeOutputHook,
55    SkinAddFooterLinksHook,
56    SkinCopyrightFooterMessageHook,
57    SkinTemplateNavigation__UniversalHook,
58    SpecialPageBeforeExecuteHook,
59    UploadForm_initialHook
60{
61
62    public const CONSTRUCTOR_OPTIONS = [
63        MainConfigNames::DBname,
64        MainConfigNames::ForceUIMsgAsContentMsg,
65        'WikimediaMessagesLicensing',
66        MainConfigNames::LanguageCode,
67        MainConfigNames::RightsText,
68        MainConfigNames::RightsPage,
69        MainConfigNames::RightsUrl,
70    ];
71
72    public function __construct(
73        private readonly ExtensionRegistry $extensionRegistry,
74        private readonly LinkRenderer $linkRenderer,
75        private readonly PermissionManager $permissionManager,
76        private readonly ServiceOptions $options,
77        private readonly UserOptionsLookup $userOptionsLookup,
78        private readonly ?MobileContext $mobileContext = null,
79    ) {
80        $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
81    }
82
83    /**
84     * @param LinkRenderer $linkRenderer
85     * @param Config $mainConfig
86     * @param PermissionManager $permissionManager
87     * @param UserOptionsLookup $userOptionsLookup
88     * @param ?MobileContext $mobileContext
89     * @return self
90     */
91    public static function factory(
92        LinkRenderer $linkRenderer,
93        Config $mainConfig,
94        PermissionManager $permissionManager,
95        UserOptionsLookup $userOptionsLookup,
96        ?MobileContext $mobileContext = null,
97    ): self {
98        return new self(
99            ExtensionRegistry::getInstance(),
100            $linkRenderer,
101            $permissionManager,
102            new ServiceOptions( self::CONSTRUCTOR_OPTIONS, $mainConfig ),
103            $userOptionsLookup,
104            $mobileContext,
105        );
106    }
107
108    /**
109     * When core requests certain messages, change the key to a Wikimedia version.
110     *
111     * @see https://www.mediawiki.org/wiki/Manual:Hooks/MessageCacheFetchOverrides
112     * @param string[] &$keys
113     */
114    public function onMessageCacheFetchOverrides( array &$keys ): void {
115        global $wmgRealm, $wgNoticeProject;
116
117        static $keysToOverride = [
118            'acct_creation_throttle_hit',
119            'acct_creation_throttle_hit-temp',
120            'centralauth-contribs-locked',
121            // T216347
122            'centralauth-login-error-locked',
123            // T86741
124            'centralauth-groupname',
125            // T345549
126            'checkuser-api-useragent-clienthints-explanation',
127            'checkuser-toollinks',
128            // T380562
129            'checkuser-global-contributions-anon-tools',
130            'checkuser-global-contributions-ip-range-tools',
131            'checkuser-global-contributions-registered-user-tools',
132            // T327281
133            'checkuser-tempaccount-enable-preference-description',
134            // T380954
135            'checkuser-temporary-accounts-onboarding-dialog-temp-accounts-step-content',
136            'checkuser-temporary-accounts-onboarding-dialog-ip-info-step-content',
137            // T380955
138            'checkuser-temporary-accounts-onboarding-dialog-ip-reveal-step-content',
139            'checkuser-temporary-accounts-onboarding-dialog-ip-reveal-step-content-with-global-preferences',
140            // T375115
141            'checkuser-temporary-account-autorevoke-userright-reason',
142            // T380518
143            'checkuser-global-contributions-no-results-no-permissions',
144            // T362548
145            'checkuser-investigateblock-warning-ips-and-users-in-targets',
146            // T420537
147            'checkuser-suggestedinvestigations-summary',
148            'createacct-helpusername',
149            'createacct-imgcaptcha-help',
150            // T139797
151            'delete-toobig',
152            // T385811
153            'vector-night-mode-issue-reporting-notice-url',
154            // T390662
155            'emailauth-login-help',
156            'feedback-termsofuse',
157            'globalblocking-blockedtext-ip',
158            'globalblocking-blockedtext-range',
159            'globalblocking-blockedtext-xff',
160            'globalblocking-blockedtext-user',
161            'globalblocking-blockedtext-autoblock',
162            'globalblocking-blockedtext-autoblock-xff',
163            'globalrenamequeue-email-body-approved',
164            'globalrenamequeue-email-body-rejected',
165            'interfaceadmin-info',
166            // T121639
167            'ipb-confirmhideuser',
168            'mwoauth-form-privacypolicy-link',
169            // T252375
170            'mobile-frontend-terms-url',
171            'mwoauthserver-invalid-user',
172            // T226059
173            'oathauth-step1-test',
174            // T189924, T406314
175            'oathauth-recovery-code-hint',
176            // T272412
177            'prefs-user-downloaddata-help-message',
178            'privacypage',
179            'sidebar',
180            // T140941
181            'sitematrix',
182            'sitematrix-others',
183            'sitematrix-summary',
184            'sulrenamewarning-usenew',
185            // T218160
186            'log-action-filter-usermerge',
187            'log-action-filter-usermerge-mergeuser',
188            'log-action-filter-usermerge-deleteuser',
189            'notification-body-oathauth-disable-other',
190            'oathauth-notifications-disable-helplink',
191            // T307493, T375090
192            'ipinfo-help-text',
193            'ipinfo-infobox-use-terms',
194            'ipinfo-preference-use-agreement',
195            'similareditors-error-default',
196            // T348926
197            'jsonconfig-license-copyrightwarning',
198            'jsonconfig-license-copyrightwarning-license-unset',
199            'jsonconfig-license-notice',
200            'jsonconfig-license-notice-license-unset',
201            // T340115
202            'campaignevents-edit-field-clickwrap-checkbox-label',
203            // T322209
204            'campaignevents-edit-form-questions-pii-notice',
205            // T357869
206            'pt-movepage-page-count-limit',
207            'skin-minerva-donate-banner-message',
208            'skin-minerva-donate-banner-subtitle',
209            'skin-minerva-donate-banner-gif',
210            'skin-minerva-donate-banner-reduced-motion',
211            'skin-minerva-donate-banner-url',
212            'namespaceinfo-description-ns470',
213            // T395565
214            'pvi-month-count-value',
215            // T399742
216            'emailauth-accountrecovery-intro',
217            'emailauth-accountrecovery-registeredemail-label',
218            // T410341
219            'oathauth-delete-warning-final-privileged-user',
220            // T416484
221            'oathauth-notifications-recoverycodes-generated-for-user-contacturl',
222            'oathauth-recover-email-text-site-admin-contact',
223            'oathauth-recover-email-text-please-contact-with-address',
224            'oathauth-notifications-recoverycodes-generated-for-user-contact',
225            // T410807
226            'notificationemail_html_footer',
227            'notificationemail_body_changed',
228            'notificationemail_body_removed',
229            // T418586, T423118
230            "userrights-restricted-group-centralnoticeadmin",
231            "userrights-restricted-group-checkuser",
232            "userrights-restricted-group-editor",
233            "userrights-restricted-group-interface-admin",
234            "userrights-restricted-group-oauthadmin",
235            "userrights-restricted-group-suppress",
236            "userrights-restricted-group-steward",
237            "userrights-restricted-group-translationadmin",
238            "userrights-restricted-group-wikidata-staff",
239            "userrights-restricted-group-wikifunctions-staff",
240            "userrights-restricted-group-wmf-officeit",
241            "userrights-restricted-group-wmf-supportsafety",
242            "userrights-restricted-group-centralnoticeadmin-private-conditions",
243            "userrights-restricted-group-checkuser-private-conditions",
244            "userrights-restricted-group-editor-private-conditions",
245            "userrights-restricted-group-interface-admin-private-conditions",
246            "userrights-restricted-group-oauthadmin-private-conditions",
247            "userrights-restricted-group-suppress-private-conditions",
248            "userrights-restricted-group-steward-private-conditions",
249            "userrights-restricted-group-translationadmin-private-conditions",
250            "userrights-restricted-group-wikidata-staff-private-conditions",
251            "userrights-restricted-group-wikifunctions-staff-private-conditions",
252            "userrights-restricted-group-wmf-officeit-private-conditions",
253            "userrights-restricted-group-wmf-supportsafety-private-conditions",
254        ];
255
256        static $allbutmetawikikeys = [
257            // T221526
258            'apierror-urlshortener-disabled',
259            // T230253
260            'apierror-urlshortener-permissiondenied',
261            // T331743
262            'centralauth-admin-log-otherwiki',
263            // T230253
264            'urlshortener-badaccessgroups',
265            // T221526
266            'urlshortener-disabled',
267        ];
268
269        // Temporarily override grouppage-suppress to grouppage-oversight,
270        // to avoid breaking links. See T112147.
271        $keys['grouppage-suppress'] = 'grouppage-oversight';
272
273        if ( $wmgRealm === 'labs' ) {
274            $keys['privacypage'] = 'wikimedia-privacypage-labs';
275        }
276
277        $languageCode = $this->options->get( MainConfigNames::LanguageCode );
278
279        $transformationCallback = static function ( string $key, MessageCache $cache ) use ( $languageCode ): string {
280            $transformedKey = "wikimedia-$key";
281
282            // MessageCache uses ucfirst if ord( key ) is < 128, which is true of all
283            // of the above.  Revisit if non-ASCII keys are used.
284            $ucKey = ucfirst( $key );
285
286            if (
287                /*
288                 * Override order:
289                 * 1. If the MediaWiki:$ucKey page exists, use the key unprefixed
290                 * (in all languages) with normal fallback order.  Specific
291                 * language pages (MediaWiki:$ucKey/xy) are not checked when
292                 * deciding which key to use, but are still used if applicable
293                 * after the key is decided.
294                 *
295                 * 2. Otherwise, use the prefixed key with normal fallback order
296                 * (including MediaWiki pages if they exist).
297                 */
298                $cache->getMsgFromNamespace( $ucKey, $languageCode ) === false
299            ) {
300                return $transformedKey;
301            }
302
303            return $key;
304        };
305
306        foreach ( $keysToOverride as $key ) {
307            $keys[$key] = $transformationCallback;
308        }
309
310        if ( $this->options->get( MainConfigNames::DBname ) !== 'metawiki' ) {
311            foreach ( $allbutmetawikikeys as $key ) {
312                $keys[$key] = $transformationCallback;
313            }
314        }
315
316        $licensing = $this->options->get( 'WikimediaMessagesLicensing' );
317        $rightsText = $this->options->get( MainConfigNames::RightsText );
318
319        switch ( $licensing ) {
320            case 'mediawiki':
321                $keys['mobile-frontend-license-links'] = 'mediawiki.org-mobile-license-links';
322                break;
323            case 'wikidata':
324                $keys['mobile-frontend-license-links'] = 'wikidata-mobile-license-links';
325                break;
326            case 'wikifunctions':
327                $keys['mobile-frontend-license-links'] = 'wikifunctions-mobile-license-links';
328                break;
329            case 'commons':
330            case 'standard':
331                $keys['mobile-frontend-license-links'] = 'wikimedia-mobile-license-links';
332                break;
333            case 'wikinews':
334                // This is necessary because MobileFrontend doesn't always display the license based on
335                // config settings (T296791)
336                $licenseMap = [
337                    'Creative Commons Attribution 2.5' => 'wikinews-mobile-license-links',
338                    'Creative Commons Attribution 3.0' => 'wikinews-mobile-license-links-ccby30',
339                    'Creative Commons Attribution 4.0' => 'wikinews-mobile-license-links-ccby40',
340                    'Creative Commons Attribution-Share Alike 4.0' => 'wikinews-mobile-license-links-ccbysa40',
341                ];
342                if ( !isset( $licenseMap[$rightsText] ) ) {
343                    throw new ConfigException( "You must define a license message for $rightsText" );
344                }
345                $keys['mobile-frontend-license-links'] = $licenseMap[$rightsText];
346                break;
347            default:
348                throw new ConfigException( "Unknown value for WikimediaMessagesLicensing: '$licensing'" );
349        }
350
351        $keys['mainpage-title-loggedin'] = function ( string $key ): string {
352            return ( $this->mobileContext && $this->mobileContext->shouldDisplayMobileView() ) ?
353                'wikimedia-mobile-mainpage-title-loggedin' : $key;
354        };
355
356        // T391026: Use a generic message on sister projects like Wiktionary or Commons.
357        // On Wikipedia projects (determined by wgNoticeProject),
358        // keep the Wikipedia-specific message
359        $keys['skin-minerva-donate-banner-subtitle'] = (
360            $wgNoticeProject === 'wikipedia'
361        )
362            ? 'wikimedia-skin-minerva-donate-banner-subtitle'
363            : 'wikimedia-skin-minerva-donate-banner-subtitle-generic';
364    }
365
366    private function getShortRightsLink(): string {
367        $rightsText = $this->options->get( MainConfigNames::RightsText );
368        $rightsPage = $this->options->get( MainConfigNames::RightsPage );
369        $rightsUrl = $this->options->get( MainConfigNames::RightsUrl );
370        $commonLicenses = [
371            'Creative Commons Attribution-Share Alike 4.0' => 'CC BY-SA 4.0',
372            'Creative Commons Attribution-Share Alike 3.0' => 'CC BY-SA 3.0',
373            'Creative Commons Attribution 3.0' => 'CC BY 3.0',
374            'Creative Commons Attribution 2.5' => 'CC BY 2.5',
375        ];
376        if ( isset( $commonLicenses[$rightsText] ) ) {
377            $rightsText = $commonLicenses[$rightsText];
378        }
379        if ( $rightsPage ) {
380            $title = Title::newFromText( $rightsPage );
381            $link = $this->linkRenderer->makeKnownLink( $title, new HtmlArmor( $rightsText ), [] );
382        } elseif ( $rightsUrl ) {
383            $link = Linker::makeExternalLink( $rightsUrl, $rightsText, true, '', [] );
384        } else {
385            $link = $rightsText;
386        }
387        return $link;
388    }
389
390    /**
391     * Override for copyright message in skin footer.
392     *
393     * @see https://www.mediawiki.org/wiki/Manual:Hooks/SkinCopyrightFooterMessage
394     *
395     * @param Title $title
396     * @param string $type
397     * @param MessageSpecifier &$msgSpec
398     */
399    public function onSkinCopyrightFooterMessage( $title, $type, &$msgSpec ) {
400        if ( $type === 'history' ) {
401            return;
402        }
403
404        $licensing = $this->options->get( 'WikimediaMessagesLicensing' );
405        $isMobile = $this->mobileContext && $this->mobileContext->shouldDisplayMobileView();
406
407        switch ( $licensing ) {
408            case 'wikidata':
409                // Wikidata is licensed under CC-BY-SA 4.0 only, no GFDL. (Also, the data is under CC0.)
410                $msgSpec = Message::newFromSpecifier( 'wikidata-copyright-footer' );
411                break;
412            case 'mediawiki':
413                // MediaWiki.org has a special licence for the Help: namespace.
414                $msgSpec = Message::newFromSpecifier( 'mediawiki.org-copyright-footer' );
415                break;
416            case 'commons':
417                // Commons has a special licence for the structured data.
418                // TODO: Should we also mention the special Data: namespace?
419                $msgSpec = Message::newFromSpecifier( 'wikimedia-commons-copyright-footer' );
420                break;
421            case 'standard':
422                // Almost all Wikimedia wikis using CC-BY-SA 4.0 are also dual-licensed under GFDL.
423                $msgSpec = $isMobile
424                    ? Message::newFromSpecifier( 'mobile-frontend-copyright' )->rawParams( $this->getShortRightsLink() )
425                    : Message::newFromSpecifier( 'wikimedia-copyright-footer' );
426                break;
427            case 'wikinews':
428                // Use the default MediaWiki message. (It's overridden locally on most Wikinewses.)
429                $msgSpec = $isMobile
430                    ? Message::newFromSpecifier( 'mobile-frontend-copyright' )->rawParams( $this->getShortRightsLink() )
431                    : $msgSpec;
432                break;
433            case 'wikifunctions':
434                // Wikifunctions like Wikidata is licensed under CC-BY-SA 4.0 only, no GFDL. The data is
435                // under CC0. The code is under Apache 2.0.
436                $msgSpec = Message::newFromSpecifier( 'wikifunctions-site-footer-copyright-footer' );
437                break;
438            default:
439                throw new ConfigException( "Unknown value for WikimediaMessagesLicensing: '$licensing'" );
440        }
441    }
442
443    /**
444     * Override for copyright message on edit page.
445     *
446     * @see https://www.mediawiki.org/wiki/Manual:Hooks/EditPageCopyrightWarning
447     *
448     * @param Title $title
449     * @param array &$msg
450     */
451    public function onEditPageCopyrightWarning( $title, &$msg ) {
452        $licensing = $this->options->get( 'WikimediaMessagesLicensing' );
453
454        switch ( $licensing ) {
455            case 'wikidata':
456            case 'wikifunctions':
457                // Wikidata and Wikifunctions are licensed under CC-BY-SA 4.0 only, no GFDL.
458                $msg = [ 'wikimedia-copyrightwarning-ccbysa40only' ];
459                break;
460            case 'mediawiki':
461                // MediaWiki.org pages are dual-licensed under CC-BY-SA 4.0 and GFDL, except for the Help: namespace.
462                $msg = [ 'mediawiki.org-copyrightwarning' ];
463                break;
464            case 'commons':
465            case 'standard':
466                // Almost all Wikimedia wikis using CC-BY-SA 4.0 are also dual-licensed under GFDL.
467                $msg = [ 'wikimedia-copyrightwarning' ];
468                break;
469            case 'wikinews':
470                // Use the default MediaWiki message. (It's overridden locally on most Wikinewses.)
471                break;
472            default:
473                throw new ConfigException( "Unknown value for WikimediaMessagesLicensing: '$licensing'" );
474        }
475    }
476
477    /**
478     * Override for copyright messages (DiscussionTools extension).
479     *
480     * @param array &$messages
481     * @param MessageLocalizer $context
482     * @param Config $config
483     */
484    public static function onDiscussionToolsTermsOfUseMessages(
485        array &$messages, MessageLocalizer $context, Config $config
486    ) {
487        $licensing = $config->get( 'WikimediaMessagesLicensing' );
488
489        switch ( $licensing ) {
490            case 'wikidata':
491            case 'wikifunctions':
492                // Do not use $wgRightsText or $wgRightsPage, as they are customized for the API (T112606).
493                $rightsUrl = $config->get( MainConfigNames::RightsUrl );
494                $rightsText = 'Creative Commons Attribution-Share Alike 4.0';
495                $links = [ "[$rightsUrl $rightsText]", 1 ];
496                break;
497            case 'wikinews':
498                // Add the license name to the terms-of-use message
499                $rightsUrl = $config->get( MainConfigNames::RightsUrl );
500                $rightsText = $config->get( MainConfigNames::RightsText );
501                $links = [ "[$rightsUrl $rightsText]", 1 ];
502                break;
503            case 'commons':
504            case 'mediawiki':
505            case 'standard':
506                // Add the license names to the terms-of-use message - dual-licensed wikis
507                $links = [ $context->msg( 'wikimedia-license-links' )->plain(), 2 ];
508                break;
509            default:
510                throw new ConfigException( "Unknown value for WikimediaMessagesLicensing: '$licensing'" );
511        }
512
513        $messages['reply'] = array_merge( [ 'wikimedia-discussiontools-replywidget-terms-click',
514            $context->msg( 'discussiontools-replywidget-reply' )->text() ], $links );
515        $messages['newtopic'] = array_merge( [ 'wikimedia-discussiontools-replywidget-terms-click',
516            $context->msg( 'discussiontools-replywidget-newtopic' )->text() ], $links );
517    }
518
519    /**
520     * Override for copyright messages (Flow extension).
521     *
522     * @param array &$messages
523     * @param MessageLocalizer $context
524     * @param Config $config
525     */
526    public static function onFlowTermsOfUseMessages(
527        array &$messages, MessageLocalizer $context, Config $config
528    ) {
529        $licensing = $config->get( 'WikimediaMessagesLicensing' );
530
531        switch ( $licensing ) {
532            case 'wikidata':
533            case 'wikifunctions':
534                // Do not use $wgRightsText or $wgRightsPage, as they are customized for the API (T112606).
535                $rightsUrl = $config->get( MainConfigNames::RightsUrl );
536                $rightsText = 'Creative Commons Attribution-Share Alike 4.0';
537                $links = [ "[$rightsUrl $rightsText]", 1 ];
538                break;
539            case 'wikinews':
540                // Add the license name to the terms-of-use message
541                $rightsUrl = $config->get( MainConfigNames::RightsUrl );
542                $rightsText = $config->get( MainConfigNames::RightsText );
543                $links = [ "[$rightsUrl $rightsText]", 1 ];
544                break;
545            case 'commons':
546            case 'mediawiki':
547            case 'standard':
548                // Add the license names to the terms-of-use message - dual-licensed wikis
549                $links = [ $context->msg( 'wikimedia-license-links' )->plain(), 2 ];
550                break;
551            default:
552                throw new ConfigException( "Unknown value for WikimediaMessagesLicensing: '$licensing'" );
553        }
554
555        $messages['edit'] = array_merge( [ 'wikimedia-flow-terms-of-use-edit' ], $links );
556        $messages['lock-topic'] = array_merge( [ 'wikimedia-flow-terms-of-use-lock-topic' ], $links );
557        $messages['new-topic'] = array_merge( [ 'wikimedia-flow-terms-of-use-new-topic' ], $links );
558        $messages['reply'] = array_merge( [ 'wikimedia-flow-terms-of-use-reply' ], $links );
559        $messages['summarize'] = array_merge( [ 'wikimedia-flow-terms-of-use-summarize' ], $links );
560        $messages['unlock-topic'] = array_merge( [ 'wikimedia-flow-terms-of-use-unlock-topic' ], $links );
561    }
562
563    /**
564     * Add links to the footer of every page:
565     *   - "Developers" (T35464),
566     *   - "Statistics" (T235803), and
567     *   - "Cookie statement" (T124366)
568     *
569     * @see https://www.mediawiki.org/wiki/Manual:Hooks/SkinAddFooterLinks
570     *
571     * @param Skin $skin
572     * @param string $key
573     * @param array &$footerLinks
574     */
575    public function onSkinAddFooterLinks( Skin $skin, string $key, array &$footerLinks ) {
576        if ( $key !== 'places' ) {
577            return;
578        }
579        $devDestination = Skin::makeInternalOrExternalUrl(
580            $skin->msg( 'wikimedia-developers-url' )->inContentLanguage()->text()
581        );
582        $footerLinks['developers'] = Html::element(
583            'a',
584            [ 'href' => $devDestination ],
585            $skin->msg( 'wikimedia-developers' )->text()
586        );
587
588        // T401547: Instead of just reading $wgServerName which is overridden when on the shared
589        // domain, let's get the current wiki's ID from the shared domain perspective and extract
590        // the hostname from there.
591        $wiki = WikiMap::getWiki( WikiMap::getCurrentWikiId() );
592        if ( !$wiki ) {
593            // Fallback to the default server name of the current domain
594            $serverName = $skin->getConfig()->get( MainConfigNames::ServerName );
595        } else {
596            $serverName = $wiki->getDisplayName();
597        }
598
599        $statsDestination = Skin::makeInternalOrExternalUrl(
600            $skin->msg( 'wikimedia-statslink-url', $serverName )->inContentLanguage()->text()
601        );
602        $footerLinks['statslink'] = Html::element(
603            'a',
604            [ 'href' => $statsDestination ],
605            $skin->msg( 'wikimedia-statslink' )->text()
606        );
607
608        $cookieDestination = Skin::makeInternalOrExternalUrl(
609            $skin->msg( 'wikimedia-cookiestatement-page' )->inContentLanguage()->text()
610        );
611        $footerLinks['cookiestatement'] = Html::element(
612            'a',
613            [ 'href' => $cookieDestination ],
614            $skin->msg( 'wikimedia-cookiestatement' )->text()
615        );
616    }
617
618    /**
619     * Change which message is shown when TorBlock triggers (TorBlock extension)
620     *
621     * @todo FIXME: Should have a specific message for WMF projects (T44231)
622     *
623     * @todo once TorBlock updates to use hook interfaces that can be implemented,
624     * convert to using this class as a hook handler and make non-static with DI
625     *
626     * @param string &$msg The message key
627     */
628    public static function onTorBlockBlockedMsg( &$msg ) {
629        $msg = 'wikimedia-torblock-blocked';
630    }
631
632    /**
633     * Require the creation of MediaWiki:Licenses to enable uploading.
634     * This should prevent a lot of uploading without licenses on small wikis;
635     * some or many of the small WMF wikis do not have any license options,
636     * which is really needed for our copyright policy.
637     *
638     * Do not require it when licenses is in $wgForceUIMsgAsContentMsg,
639     * to prevent checking each subpage of MediaWiki:Licenses.
640     *
641     * @param SpecialUpload $upload
642     * @throws ErrorPageError
643     */
644    public function onUploadForm_initial( $upload ) {
645        $forceUIMsgAsContentMsg = $this->options->get( MainConfigNames::ForceUIMsgAsContentMsg );
646
647        // TODO inject something to replace use of wfMessage
648        if ( !in_array( 'licenses', $forceUIMsgAsContentMsg )
649            && wfMessage( 'licenses' )->inContentLanguage()->isDisabled()
650        ) {
651            throw new ErrorPageError( 'uploaddisabled', 'wikimedia-upload-nolicenses' );
652        }
653    }
654
655    /**
656     * Override `$wgGrammarForms` for Wikimedia sites as a more convenient form
657     * than setting in InitialiseSettings.php. Initial version was compiled from
658     * then-current Wikimedia configuration and contents of LanguageXx.php.
659     */
660    public static function onRegistration() {
661        global $wgGrammarForms;
662
663        $wgGrammarForms['ang'] = [
664            // accusative
665            'wrēgendlīc' => [
666                'Wikipǣdia' => 'Wikipǣdie',
667                'Wikiwordbōc' => 'Wikiwordbōc',
668            ],
669            // genitive
670            'geāgniendlīc' => [
671                'Wikipǣdia' => 'Wikipǣdie',
672                'Wikiwordbōc' => 'Wikiwordbēc',
673            ],
674            // dative
675            'forgifendlīc' => [
676                'Wikipǣdia' => 'Wikipǣdie',
677                'Wikiwordbōc' => 'Wikiwordbēc',
678            ],
679            // instrumental
680            'tōllīc' => [
681                'Wikipǣdia' => 'Wikipǣdie',
682                'Wikiwordbōc' => 'Wikiwordbēc',
683            ],
684        ];
685
686        $wgGrammarForms['be'] = [
687            // genitive
688            'родны' => [
689                'Віківіды'    => 'Віківідаў',
690                'Вікіданыя'   => 'Вікіданых',
691                'Вікікнігі'   => 'Вікікніг',
692                'Вікікрыніцы' => 'Вікікрыніц',
693                'Вікінавіны'  => 'Вікінавін',
694                'Вікіслоўнік' => 'Вікіслоўніка',
695                'Вікіпедыя'   => 'Вікіпедыі',
696            ],
697            // accusative
698            'вінавальны' => [
699                'Вікіпедыя'   => 'Вікіпедыю',
700            ],
701            // prepositional
702            'месны' => [
703                'Віківіды'    => 'Віківідах',
704                'Вікіданыя'   => 'Вікіданых',
705                'Вікікнігі'   => 'Вікікнігах',
706                'Вікікрыніцы' => 'Вікікрыніцах',
707                'Вікінавіны'  => 'Вікінавінах',
708                'Вікіслоўнік' => 'Вікіслоўніку',
709                'Вікіпедыя'   => 'Вікіпедыі',
710            ],
711        ];
712
713        $wgGrammarForms['be-tarask'] = [
714            // genitive
715            'родны' => [
716                'Віківіды'    => 'Віківідаў',
717                'Вікізьвесткі' => 'Вікізьвестак',
718                'Вікікнігі'   => 'Вікікніг',
719                'Вікікрыніцы' => 'Вікікрыніц',
720                'Вікімэдыя'   => 'Вікімэдыі',
721                'Вікінавіны'  => 'Вікінавін',
722                'Вікіпэдыя'   => 'Вікіпэдыі',
723                'Вікіслоўнік' => 'Вікіслоўніка',
724                'Вікісховішча' => 'Вікісховішча',
725                'Фундацыя «Вікімэдыя»' => 'Фундацыі «Вікімэдыя»',
726            ],
727            // dative
728            'давальны' => [
729                'Віківіды'    => 'Віківідам',
730                'Вікізьвесткі' => 'Вікізьвесткам',
731                'Вікікнігі'   => 'Вікікнігам',
732                'Вікікрыніцы' => 'Вікікрыніцам',
733                'Вікімэдыя'   => 'Вікімэдыі',
734                'Вікінавіны'  => 'Вікінавінам',
735                'Вікіпэдыя'   => 'Вікіпэдыі',
736                'Вікіслоўнік' => 'Вікіслоўніку',
737                'Вікісховішча' => 'Вікісховішчу',
738                'Фундацыя «Вікімэдыя»' => 'Фундацыі «Вікімэдыя»',
739            ],
740            // accusative
741            'вінавальны' => [
742                'Віківіды'    => 'Віківіды',
743                'Вікізьвесткі' => 'Вікізьвесті',
744                'Вікікнігі'   => 'Вікікнігі',
745                'Вікікрыніцы' => 'Вікікрыніцы',
746                'Вікімэдыя'   => 'Вікімэдыю',
747                'Вікінавіны'  => 'Вікінавіны',
748                'Вікіпэдыя'   => 'Вікіпэдыю',
749                'Вікіслоўнік' => 'Вікіслоўнік',
750                'Вікісховішча' => 'Вікісховішча',
751                'Фундацыя «Вікімэдыя»' => 'Фундацыю «Вікімэдыя»',
752            ],
753            // instrumental
754            'творны' => [
755                'Віківіды'    => 'Віківідамі',
756                'Вікізьвесткі' => 'Вікізьвесткамі',
757                'Вікікнігі'   => 'Вікікнігамі',
758                'Вікікрыніцы' => 'Вікікрыніцамі',
759                'Вікімэдыя'   => 'Вікімэдыяй',
760                'Вікінавіны'  => 'Вікінавінамі',
761                'Вікіпэдыя'   => 'Вікіпэдыяй',
762                'Вікіслоўнік' => 'Вікіслоўнікам',
763                'Вікісховішча' => 'Вікісховішчам',
764                'Фундацыя «Вікімэдыя»' => 'Фундацыяй «Вікімэдыя»',
765            ],
766            // prepositional
767            'месны' => [
768                'Віківіды'    => 'Віківідах',
769                'Вікізьвесткі' => 'Вікізьвестках',
770                'Вікікнігі'   => 'Вікікнігах',
771                'Вікікрыніцы' => 'Вікікрыніцах',
772                'Вікімэдыя'   => 'Вікімэдыі',
773                'Вікінавіны'  => 'Вікінавінах',
774                'Вікіпэдыя'   => 'Вікіпэдыі',
775                'Вікіслоўнік' => 'Вікіслоўніку',
776                'Вікісховішча' => 'Вікісховішчы',
777                'Фундацыя «Вікімэдыя»' => 'Фундацыі «Вікімэдыя»',
778            ],
779        ];
780
781        $wgGrammarForms['bs'] = [
782            // genitive
783            'genitiv' => [
784                'Vikirječnik' => 'Wikirječnika',
785                'Wikicitati'  => 'Wikicitata',
786                'Wikiizvor'   => 'Wikiizvora',
787                'Wikiknjige'  => 'Wikiknjiga',
788                'Wikipedia'   => 'Wikipedije',
789                'Wikipodaci'  => 'Wikipodataka',
790                'Wikimedia Commons' => 'Wikimedia Commonsa',
791            ],
792            // dative
793            'dativ' => [
794                'Vikirječnik' => 'Wikirječniku',
795                'Wikicitati'  => 'Wikicitatima',
796                'Wikiizvor'   => 'Wikiizvoru',
797                'Wikiknjige'  => 'Wikiknjigama',
798                'Wikipedia'   => 'Wikipediji',
799                'Wikipodaci'  => 'Wikipodacima',
800                'Wikivijesti' => 'Wikivijestima',
801                'Wikimedia Commons' => 'Wikimedia Commonsu',
802            ],
803            // accusative
804            'akuzativ' => [
805                'Vikirječnik' => 'Wikirječnik',
806                'Wikicitati'  => 'Wikicitate',
807                'Wikiizvor'   => 'Wikiizvora',
808                'Wikipedia'   => 'Wikipediju',
809                'Wikipodaci'  => 'Wikipodatke',
810            ],
811            // vocative
812            'vokativ' => [
813                'Vikirječnik' => 'Wikirječniče',
814                'Wikiizvor'   => 'Wikizivoru',
815                'Wikipedia'   => 'Wikipedijo',
816                'Wikimedia Commons' => 'Wikimedia Commonse',
817            ],
818            // instrumental
819            'instrumental' => [
820                'Vikirječnik' => 'Wikirječnikom',
821                'Wikicitati'  => 'Wikicitatima',
822                'Wikiizvor'   => 'Wikiizvorom',
823                'Wikiknjige'  => 'Wikiknjigama',
824                // T130141
825                'Wikipedia'   => 'Wikipedijom',
826                'Wikipodaci'  => 'Wikipodacima',
827                'Wikivijesti' => 'Wikivijestima',
828                'Wikimedia Commons' => 'Wikimedia Commonsom',
829            ],
830            // locative
831            'lokativ' => [
832                'Vikirječnik' => 'Wikirječniku',
833                'Wikicitati'  => 'Wikicitatima',
834                'Wikiizvor'   => 'Wikiizvoru',
835                'Wikiknjige'  => 'Wikiknjigama',
836                'Wikipedia'   => 'Wikipediji',
837                'Wikipodaci'  => 'Wikipodacima',
838                'Wikivijesti' => 'Wikivijestima',
839                'Wikimedia Commons' => 'Wikimedia Commonsu',
840            ],
841        ];
842
843        $wgGrammarForms['cs'] = [
844            // only forms different than default/given
845            '1sg' => [
846                'Abstract Wikipedia' => 'Abstraktní Wikipedie',
847                'Wikibooks'   => 'Wikiknihy',
848                'Wikifunctions' => 'Wikifunkce',
849                'Wikinews'    => 'Wikizprávy',
850                'Wikipedia'   => 'Wikipedie',
851                'Wikiquote'   => 'Wikicitáty',
852                'Wikisource'  => 'Wikizdroje',
853                'Wikispecies' => 'Wikidruhy',
854                'Wikiversity' => 'Wikiverzita',
855                'Wikivoyage'  => 'Wikicesty',
856                'Wiktionary'  => 'Wikislovník',
857            ],
858            '2sg' => [
859                'uživatel'    => 'uživatele',
860                'Abstract Wikipedia' => 'Abstraktní Wikipedie',
861                'Wikibooks'   => 'Wikiknih',
862                'Wikidata'    => 'Wikidat',
863                'Wikifunctions' => 'Wikifunkcí',
864                'Wikinews'    => 'Wikizpráv',
865                'Wikipedia'   => 'Wikipedie',
866                'Wikiquote'   => 'Wikicitátů',
867                'Wikisource'  => 'Wikizdrojů',
868                'Wikispecies' => 'Wikidruhů',
869                'Wikiversity' => 'Wikiverzity',
870                'Wikivoyage'  => 'Wikicest',
871                'Wiktionary'  => 'Wikislovníku',
872                'Wikicesty'   => 'Wikicest',
873                'Wikicitáty'  => 'Wikicitátů',
874                'Wikidruhy'   => 'Wikidruhů',
875                'Wikifunkce'  => 'Wikifunkcí',
876                'Wikiknihy'   => 'Wikiknih',
877                'Wikislovník' => 'Wikislovníku',
878                'Wikiverzita' => 'Wikiverzity',
879                'Wikizdroje'  => 'Wikizdrojů',
880                'Wikizprávy'  => 'Wikizpráv',
881            ],
882            '3sg' => [
883                'uživatel'    => 'uživateli',
884                'Abstract Wikipedia' => 'Abstraktní Wikipedii',
885                'Wikibooks'   => 'Wikiknihám',
886                'Wikidata'    => 'Wikidatům',
887                'Wikifunctions' => 'Wikifunkcím',
888                'Wikinews'    => 'Wikizprávám',
889                'Wikipedia'   => 'Wikipedii',
890                'Wikiquote'   => 'Wikicitátům',
891                'Wikisource'  => 'Wikizdrojům',
892                'Wikispecies' => 'Wikidruhům',
893                'Wikiversity' => 'Wikiverzitě',
894                'Wikivoyage'  => 'Wikicestám',
895                'Wiktionary'  => 'Wikislovníku',
896                'Abstraktní Wikipedie' => 'Abstraktní Wikipedii',
897                'Wikicesty'   => 'Wikicestám',
898                'Wikicitáty'  => 'Wikicitátům',
899                'Wikidruhy'   => 'Wikidruhům',
900                'Wikifunkce'  => 'Wikifunkcím',
901                'Wikiknihy'   => 'Wikiknihám',
902                'Wikipedie'   => 'Wikipedii',
903                'Wikislovník' => 'Wikislovníku',
904                'Wikiverzita' => 'Wikiverzitě',
905                'Wikizdroje'  => 'Wikizdrojům',
906                'Wikizprávy'  => 'Wikizprávám',
907            ],
908            '4sg' => [
909                'uživatel'    => 'uživatele',
910                'Abstract Wikipedia' => 'Abstraktní Wikipedii',
911                'Wikibooks'   => 'Wikiknihy',
912                'Wikifunctions' => 'Wikifunkce',
913                'Wikinews'    => 'Wikizprávy',
914                'Wikipedia'   => 'Wikipedii',
915                'Wikiquote'   => 'Wikicitáty',
916                'Wikisource'  => 'Wikizdroje',
917                'Wikispecies' => 'Wikidruhy',
918                'Wikiversity' => 'Wikiverzitu',
919                'Wikivoyage'  => 'Wikicesty',
920                'Wiktionary'  => 'Wikislovník',
921                'Abstraktní Wikipedie' => 'Abstraktní Wikipedii',
922                'Wikipedie'   => 'Wikipedii',
923                'Wikiverzita' => 'Wikiverzitu',
924            ],
925            '5sg' => [
926                'uživatel'    => 'uživateli',
927                'Abstract Wikipedia' => 'Abstraktní Wikipedie',
928                'Wikibooks'   => 'Wikiknihy',
929                'Wikifunctions' => 'Wikifunkce',
930                'Wikinews'    => 'Wikizprávy',
931                'Wikipedia'   => 'Wikipedie',
932                'Wikiquote'   => 'Wikicitáty',
933                'Wikisource'  => 'Wikizdroje',
934                'Wikispecies' => 'Wikidruhy',
935                'Wikiversity' => 'Wikiverzito',
936                'Wikivoyage'  => 'Wikicesty',
937                'Wiktionary'  => 'Wikislovníku',
938                'Wikislovník' => 'Wikislovníku',
939                'Wikiverzita' => 'Wikiverzito',
940            ],
941            '6sg' => [
942                'uživatel'    => 'uživateli',
943                'Abstract Wikipedia' => 'Abstraktní Wikipedii',
944                'Wikibooks'   => 'Wikiknihách',
945                'Wikidata'    => 'Wikidatech',
946                'Wikifunctions' => 'Wikifunkcích',
947                'Wikinews'    => 'Wikizprávách',
948                'Wikipedia'   => 'Wikipedii',
949                'Wikiquote'   => 'Wikicitátech',
950                'Wikisource'  => 'Wikizdrojích',
951                'Wikispecies' => 'Wikidruzích',
952                'Wikiversity' => 'Wikiverzitě',
953                'Wikivoyage'  => 'Wikicestách',
954                'Wiktionary'  => 'Wikislovníku',
955                'Abstraktní Wikipedie' => 'Abstraktní Wikipedii',
956                'Wikicesty'   => 'Wikicestách',
957                'Wikicitáty'  => 'Wikicitátech',
958                'Wikidruhy'   => 'Wikidruzích',
959                'Wikifunkce'  => 'Wikifunkcích',
960                'Wikiknihy'   => 'Wikiknihách',
961                'Wikipedie'   => 'Wikipedii',
962                'Wikislovník' => 'Wikislovníku',
963                'Wikiverzita' => 'Wikiverzitě',
964                'Wikizdroje'  => 'Wikizdrojích',
965                'Wikizprávy'  => 'Wikizprávách',
966            ],
967            '7sg' => [
968                'uživatel'    => 'uživatelem',
969                'Abstract Wikipedia' => 'Abstraktní Wikipedií',
970                'Wikibooks'   => 'Wikiknihami',
971                'Wikidata'    => 'Wikidaty',
972                'Wikifunctions' => 'Wikifunkcemi',
973                'Wikinews'    => 'Wikizprávami',
974                'Wikipedia'   => 'Wikipedií',
975                'Wikiquote'   => 'Wikicitáty',
976                'Wikisource'  => 'Wikizdroji',
977                'Wikispecies' => 'Wikidruhy',
978                'Wikiversity' => 'Wikiverzitou',
979                'Wikivoyage'  => 'Wikicestami',
980                'Wiktionary'  => 'Wikislovníkem',
981                'Abstraktní Wikipedie' => 'Abstraktní Wikipedií',
982                'Wikicesty'   => 'Wikicestami',
983                'Wikifunkce'  => 'Wikifunkcemi',
984                'Wikiknihy'   => 'Wikiknihami',
985                'Wikipedie'   => 'Wikipedií',
986                'Wikislovník' => 'Wikislovníkem',
987                'Wikiverzita' => 'Wikiverzitou',
988                'Wikizdroje'  => 'Wikizdroji',
989                'Wikizprávy'  => 'Wikizprávami',
990            ],
991            '1pl' => [
992                'uživatel'    => 'uživatelé',
993                'Abstract Wikipedia' => 'Abstraktní Wikipedie',
994                'Wikibooks'   => 'Wikiknihy',
995                'Wikifunctions' => 'Wikifunkce',
996                'Wikinews'    => 'Wikizprávy',
997                'Wikipedia'   => 'Wikipedie',
998                'Wikiquote'   => 'Wikicitáty',
999                'Wikisource'  => 'Wikizdroje',
1000                'Wikispecies' => 'Wikidruhy',
1001                'Wikiversity' => 'Wikiverzity',
1002                'Wikivoyage'  => 'Wikicesty',
1003                'Wiktionary'  => 'Wikislovníky',
1004                'Wikislovník' => 'Wikislovníky',
1005                'Wikiverzita' => 'Wikiverzity',
1006            ],
1007            '2pl' => [
1008                'uživatel'    => 'uživatelů',
1009                'Abstract Wikipedia' => 'Abstraktních Wikipedií',
1010                'Wikibooks'   => 'Wikiknih',
1011                'Wikidata'    => 'Wikidat',
1012                'Wikifunctions' => 'Wikifunkcí',
1013                'Wikinews'    => 'Wikizpráv',
1014                'Wikipedia'   => 'Wikipedií',
1015                'Wikiquote'   => 'Wikicitátů',
1016                'Wikisource'  => 'Wikizdrojů',
1017                'Wikispecies' => 'Wikidruhů',
1018                'Wikiversity' => 'Wikiverzit',
1019                'Wikivoyage'  => 'Wikicest',
1020                'Wiktionary'  => 'Wikislovníků',
1021                'Abstraktní Wikipedie' => 'Abstraktních Wikipedií',
1022                'Wikicesty'   => 'Wikicest',
1023                'Wikicitáty'  => 'Wikicitátů',
1024                'Wikidruhy'   => 'Wikidruhů',
1025                'Wikifunkce'  => 'Wikifunkcí',
1026                'Wikiknihy'   => 'Wikiknih',
1027                'Wikipedie'   => 'Wikipedií',
1028                'Wikislovník' => 'Wikislovníků',
1029                'Wikiverzita' => 'Wikiverzit',
1030                'Wikizdroje'  => 'Wikizdrojů',
1031                'Wikizprávy'  => 'Wikizpráv',
1032            ],
1033            '3pl' => [
1034                'uživatel'    => 'uživatelům',
1035                'Abstract Wikipedia' => 'Abstraktním Wikipediím',
1036                'Wikibooks'   => 'Wikiknihám',
1037                'Wikidata'    => 'Wikidatům',
1038                'Wikifunctions' => 'Wikifunkcím',
1039                'Wikinews'    => 'Wikizprávám',
1040                'Wikipedia'   => 'Wikipediím',
1041                'Wikiquote'   => 'Wikicitátům',
1042                'Wikisource'  => 'Wikizdrojům',
1043                'Wikispecies' => 'Wikidruhům',
1044                'Wikiversity' => 'Wikiverzitám',
1045                'Wikivoyage'  => 'Wikicestám',
1046                'Wiktionary'  => 'Wikislovníkům',
1047                'Abstraktní Wikipedie' => 'Abstraktním Wikipediím',
1048                'Wikicesty'   => 'Wikicestám',
1049                'Wikicitáty'  => 'Wikicitátům',
1050                'Wikidruhy'   => 'Wikidruhům',
1051                'Wikifunkce'  => 'Wikifunkcím',
1052                'Wikiknihy'   => 'Wikiknihám',
1053                'Wikipedie'   => 'Wikipediím',
1054                'Wikislovník' => 'Wikislovníkům',
1055                'Wikiverzita' => 'Wikiverzitám',
1056                'Wikizdroje'  => 'Wikizdrojům',
1057                'Wikizprávy'  => 'Wikizprávám',
1058            ],
1059            '4pl' => [
1060                'uživatel'    => 'uživatele',
1061                'Abstract Wikipedia' => 'Abstraktní Wikipedie',
1062                'Wikibooks'   => 'Wikiknihy',
1063                'Wikifunctions' => 'Wikifunkce',
1064                'Wikinews'    => 'Wikizprávy',
1065                'Wikipedia'   => 'Wikipedie',
1066                'Wikiquote'   => 'Wikicitáty',
1067                'Wikisource'  => 'Wikizdroje',
1068                'Wikispecies' => 'Wikidruhy',
1069                'Wikiversity' => 'Wikiverzity',
1070                'Wikivoyage'  => 'Wikicesty',
1071                'Wiktionary'  => 'Wikislovníky',
1072                'Wikislovník' => 'Wikislovníky',
1073                'Wikiverzita' => 'Wikiverzity',
1074            ],
1075            '5pl' => [
1076                'uživatel'    => 'uživatelé',
1077                'Abstract Wikipedia' => 'Abstraktní Wikipedie',
1078                'Wikibooks'   => 'Wikiknihy',
1079                'Wikifunctions' => 'Wikifunkce',
1080                'Wikinews'    => 'Wikizprávy',
1081                'Wikipedia'   => 'Wikipedie',
1082                'Wikiquote'   => 'Wikicitáty',
1083                'Wikisource'  => 'Wikizdroje',
1084                'Wikispecies' => 'Wikidruhy',
1085                'Wikiversity' => 'Wikiverzity',
1086                'Wikivoyage'  => 'Wikicesty',
1087                'Wiktionary'  => 'Wikislovníky',
1088                'Wikislovník' => 'Wikislovníky',
1089                'Wikiverzita' => 'Wikiverzity',
1090            ],
1091            '6pl' => [
1092                'uživatel'    => 'uživatelích',
1093                'Abstract Wikipedia' => 'Abstraktních Wikipediích',
1094                'Wikibooks'   => 'Wikiknihách',
1095                'Wikidata'    => 'Wikidatech',
1096                'Wikifunctions' => 'Wikifunkcích',
1097                'Wikinews'    => 'Wikizprávách',
1098                'Wikipedia'   => 'Wikipediích',
1099                'Wikiquote'   => 'Wikicitátech',
1100                'Wikisource'  => 'Wikizdrojích',
1101                'Wikispecies' => 'Wikidruzích',
1102                'Wikiversity' => 'Wikiverzitách',
1103                'Wikivoyage'  => 'Wikicestách',
1104                'Wiktionary'  => 'Wikislovnících',
1105                'Abstraktní Wikipedie' => 'Abstraktních Wikipediích',
1106                'Wikicesty'   => 'Wikicestách',
1107                'Wikicitáty'  => 'Wikicitátech',
1108                'Wikidruhy'   => 'Wikidruzích',
1109                'Wikifunkce'  => 'Wikifunkcích',
1110                'Wikiknihy'   => 'Wikiknihách',
1111                'Wikipedie'   => 'Wikipediích',
1112                'Wikislovník' => 'Wikislovnících',
1113                'Wikiverzita' => 'Wikiverzitách',
1114                'Wikizdroje'  => 'Wikizdrojích',
1115                'Wikizprávy'  => 'Wikizprávách',
1116            ],
1117            '7pl' => [
1118                'uživatel'    => 'uživateli',
1119                'Abstract Wikipedia' => 'Abstraktními Wikipediemi',
1120                'Wikibooks'   => 'Wikiknihami',
1121                'Wikidata'    => 'Wikidaty',
1122                'Wikifunctions' => 'Wikifunkcemi',
1123                'Wikinews'    => 'Wikizprávami',
1124                'Wikipedia'   => 'Wikipediemi',
1125                'Wikiquote'   => 'Wikicitáty',
1126                'Wikisource'  => 'Wikizdroji',
1127                'Wikispecies' => 'Wikidruhy',
1128                'Wikiversity' => 'Wikiverzitami',
1129                'Wikivoyage'  => 'Wikicestami',
1130                'Wiktionary'  => 'Wikislovníky',
1131                'Abstraktní Wikipedie' => 'Abstraktními Wikipediemi',
1132                'Wikicesty'   => 'Wikicestami',
1133                'Wikifunkce'  => 'Wikifunkcemi',
1134                'Wikiknihy'   => 'Wikiknihami',
1135                'Wikipedie'   => 'Wikipediemi',
1136                'Wikislovník' => 'Wikislovníky',
1137                'Wikiverzita' => 'Wikiverzitami',
1138                'Wikizdroje'  => 'Wikizdroji',
1139                'Wikizprávy'  => 'Wikizprávami',
1140            ],
1141        ];
1142
1143        $wgGrammarForms['dsb'] = [
1144            // genitive
1145            'genitiw' => [
1146                'Wikipedija'  => 'Wikipedije',
1147                'Wikiknihi'   => 'Wikiknih',
1148                'Wikinowiny'  => 'Wikinowin',
1149                'Wikižórło'   => 'Wikižórła',
1150                'Wikicitaty'  => 'Wikicitatow',
1151                'Wikisłownik' => 'Wikisłownika',
1152            ],
1153            // dative
1154            'datiw' => [
1155                'Wikipedija'  => 'Wikipediji',
1156                'Wikiknihi'   => 'Wikikniham',
1157                'Wikinowiny'  => 'Wikinowinam',
1158                'Wikižórło'   => 'Wikižórłu',
1159                'Wikicitaty'  => 'Wikicitatam',
1160                'Wikisłownik' => 'Wikisłownikej',
1161            ],
1162            // accusative
1163            'akuzativ' => [
1164                'Wikipedija'  => 'Wikipediju',
1165                'Wikiknihi'   => 'Wikiknknihi',
1166            ],
1167            // instrumental
1168            'instrumental' => [
1169                'Wikipedija'  => 'Wikipediju',
1170                'Wikiknihi'   => 'Wikiknihami',
1171                'Wikinowiny'  => 'Wikinowinami',
1172                'Wikižórło'   => 'Wikižórłom',
1173                'Wikicitaty'  => 'Wikicitatami',
1174                'Wikisłownik' => 'Wikisłownikom',
1175            ],
1176            // locative
1177            'lokatiw' => [
1178                'Wikipedija'  => 'Wikipediji',
1179                'Wikiknihi'   => 'Wikiknihach',
1180                'Wikinowiny'  => 'Wikinowinach',
1181                'Wikižórło'   => 'Wikižórłu',
1182                'Wikicitaty'  => 'Wikicitatach',
1183                'Wikisłownik' => 'Wikisłowniku',
1184            ],
1185        ];
1186
1187        $wgGrammarForms['fi'] = [
1188            'genitive' => [
1189                'Wikiuutiset' => 'Wikiuutisten',
1190                'Wikisitaatit' => 'Wikisitaattien',
1191                'Wikimedia Suomi' => 'Wikimedia Suomen',
1192                'Wikimatkat' => 'Wikimatkojen',
1193            ],
1194            'partitive' => [
1195                'Wikiuutiset' => 'Wikiuutisia',
1196                'Wikisitaatit' => 'Wikisitaatteja',
1197                'Wikimedia Suomi' => 'Wikimedia Suomea',
1198                'Wikimatkat' => 'Wikimatkoja',
1199            ],
1200            'elative' => [
1201                'Wikiuutiset' => 'Wikiuutisista',
1202                'Wikisitaatit' => 'Wikisitaateista',
1203                'Wikimedia Suomi' => 'Wikimedia Suomesta',
1204                'Wikimatkat' => 'Wikimatkoista',
1205            ],
1206            'inessive' => [
1207                'Wikiuutiset' => 'Wikiuutisissa',
1208                'Wikisitaatit' => 'Wikisitaateissa',
1209                'Wikimedia Suomi' => 'Wikimedia Suomessa',
1210                'Wikimatkat' => 'Wikimatkoissa',
1211            ],
1212            'illative' => [
1213                'Wikiuutiset' => 'Wikiuutisiin',
1214                'Wikisitaatit' => 'Wikisitaatteihin',
1215                'Wikimedia Suomi' => 'Wikimedia Suomeen',
1216                'Wikimatkat' => 'Wikimatkoihin',
1217            ],
1218        ];
1219
1220        $wgGrammarForms['ga'] = [
1221            'genitive' => [
1222                'Vicipéid'     => 'Vicipéide',
1223                'Vicífhoclóir' => 'Vicífhoclóra',
1224                'Vicíleabhair' => 'Vicíleabhar',
1225                'Vicíshliocht' => 'Vicíshleachta',
1226                'Vicífhoinse'  => 'Vicífhoinse',
1227                'Vicíghnéithe' => 'Vicíghnéithe',
1228                'Vicínuacht'   => 'Vicínuachta',
1229            ],
1230        ];
1231
1232        $wgGrammarForms['gsw'] = [
1233            // dative
1234            'dativ' => [
1235                'Wikipedia'       => 'vo de Wikipedia',
1236                'Wikinorchrichte' => 'vo de Wikinochrichte',
1237                'Wiktionaire'     => 'vom Wiktionaire',
1238                'Wikibuecher'     => 'vo de Wikibuecher',
1239                'Wikisprüch'      => 'vo de Wikisprüch',
1240                'Wikiquälle'      => 'vo de Wikiquälle',
1241            ],
1242            // accusative
1243            'akkusativ' => [
1244                'Wikipedia'       => 'd Wikipedia',
1245                'Wikinorchrichte' => 'd Wikinorchrichte',
1246                'Wiktionaire'     => 's Wiktionaire',
1247                'Wikibuecher'     => 'd Wikibuecher',
1248                'Wikisprüch'      => 'd Wikisprüch',
1249                'Wikiquälle'      => 'd Wikiquälle',
1250            ],
1251            // nominative
1252            'nominativ' => [
1253                'Wikipedia'       => 'd Wikipedia',
1254                'Wikinorchrichte' => 'd Wikinorchrichte',
1255                'Wiktionaire'     => 's Wiktionaire',
1256                'Wikibuecher'     => 'd Wikibuecher',
1257                'Wikisprüch'      => 'd Wikisprüch',
1258                'Wikiquälle'      => 'd Wikiquälle',
1259            ],
1260        ];
1261
1262        $wgGrammarForms['hsb'] = [
1263            // genitive
1264            'genitiw' => [
1265                'Wikipedija'  => 'Wikipedije',
1266                'Wikiknihi'   => 'Wikiknih',
1267                'Wikinowiny'  => 'Wikinowin',
1268                'Wikižórło'   => 'Wikižórła',
1269                'Wikicitaty'  => 'Wikicitatow',
1270                'Wikisłownik' => 'Wikisłownika',
1271            ],
1272            // dative
1273            'datiw' => [
1274                'Wikipedija'  => 'Wikipediji',
1275                'Wikiknihi'   => 'Wikikniham',
1276                'Wikinowiny'  => 'Wikinowinam',
1277                'Wikižórło'   => 'Wikižórłu',
1278                'Wikicitaty'  => 'Wikicitatam',
1279                'Wikisłownik' => 'Wikisłownikej',
1280            ],
1281            // accusative
1282            'akuzativ' => [
1283                'Wikipedija'  => 'Wikipediju',
1284                'Wikiknihi'   => 'Wikiknknihi',
1285            ],
1286            // instrumental
1287            'instrumental' => [
1288                'Wikipedija'  => 'Wikipediju',
1289                'Wikiknihi'   => 'Wikiknihami',
1290                'Wikinowiny'  => 'Wikinowinami',
1291                'Wikižórło'   => 'Wikižórłom',
1292                'Wikicitaty'  => 'Wikicitatami',
1293                'Wikisłownik' => 'Wikisłownikom',
1294            ],
1295            // locative
1296            'lokatiw' => [
1297                'Wikipedija'  => 'Wikipediji',
1298                'Wikiknihi'   => 'Wikiknihach',
1299                'Wikinowiny'  => 'Wikinowinach',
1300                'Wikižórło'   => 'Wikižórłu',
1301                'Wikicitaty'  => 'Wikicitatach',
1302                'Wikisłownik' => 'Wikisłowniku',
1303            ],
1304        ];
1305
1306        $wgGrammarForms['hu'] = [
1307            'rol' => [
1308                'Wikipédia'   => 'Wikipédiáról',
1309                'Wikidézet'   => 'Wikidézetről',
1310                'Wikiszótár'  => 'Wikiszótárról',
1311                'Wikikönyvek' => 'Wikikönyvekről',
1312            ],
1313            'ba' => [
1314                'Wikipédia'   => 'Wikipédiába',
1315                'Wikidézet'   => 'Wikidézetbe',
1316                'Wikiszótár'  => 'Wikiszótárba',
1317                'Wikikönyvek' => 'Wikikönyvekbe',
1318            ],
1319            'k' => [
1320                'Wikipédia'   => 'Wikipédiák',
1321                'Wikidézet'   => 'Wikidézetek',
1322                'Wikiszótár'  => 'Wikiszótárak',
1323            ],
1324        ];
1325        // T289767
1326        $wgGrammarForms['ks'] = [
1327            // ergative
1328            'کرٛاوَل' => [
1329                'وِکیٖلۄغَتھ' => 'وِکیٖلۄغتَن',
1330            ],
1331            // dative
1332            'دُکرٛٲوؠ' => [
1333                'وِکیٖلۄغَتھ' => 'وِکیٖلۄغتَس',
1334            ],
1335            // ablative
1336            'آیِتؠ' => [
1337                'وِکیٖلۄغَتھ' => 'وِکیٖلۄغتہٕ',
1338            ],
1339        ];
1340        $wgGrammarForms['la'] = [
1341            'genitive' => [
1342                'Vicimedia Communia' => 'Vicimediorum Communium',
1343            ],
1344            'ablative' => [
1345                'Vicimedia Communia' => 'Vicimediis Communibus',
1346            ],
1347        ];
1348
1349        $wgGrammarForms['lv'] = [
1350            'ģenitīvs' => [
1351                'Vikipēdija'   => 'Vikipēdijas',
1352                'Vikivārdnīca' => 'Vikivārdnīcas',
1353            ],
1354            'datīvs' => [
1355                'Vikipēdija'   => 'Vikipēdijai',
1356                'Vikivārdnīca' => 'Vikivārdnīcai',
1357            ],
1358            'akuzatīvs' => [
1359                'Vikipēdija'   => 'Vikipēdiju',
1360                'Vikivārdnīca' => 'Vikivārdnīcu',
1361            ],
1362            'lokatīvs' => [
1363                'Vikipēdija'   => 'Vikipēdijā',
1364                'Vikivārdnīca' => 'Vikivārdnīcā',
1365            ],
1366        ];
1367
1368        $wgGrammarForms['pl'] = [
1369            'D.lp' => [
1370                'Wikicytaty'   => 'Wikicytatów',
1371                'Wikipedia'    => 'Wikipedii',
1372                'Wikipodróże'  => 'Wikipodróży',
1373                'Wikisłownik'  => 'Wikisłownika',
1374                'Wikiźródła'   => 'Wikiźródeł',
1375                'Wikidane'     => 'Wikidanych',
1376                'Wikimania'    => 'Wikimanii',
1377                'Wikifunkcje'  => 'Wikifunkcji',
1378            ],
1379            'C.lp' => [
1380                'Wikicytaty'   => 'Wikicytatom',
1381                'Wikipedia'    => 'Wikipedii',
1382                'Wikipodróże'  => 'Wikipodróżom',
1383                'Wikisłownik'  => 'Wikisłownikowi',
1384                'Wikiźródła'   => 'Wikiźródłom',
1385                'Wikidane'     => 'Wikidanym',
1386                'Wikimania'    => 'Wikimanii',
1387                'Wikifunkcje'  => 'Wikifunkcjom',
1388            ],
1389            'B.lp' => [
1390                'Wikipedia'    => 'Wikipedię',
1391                'Wikimania'    => 'Wikimanię',
1392            ],
1393            'N.lp' => [
1394                'Wikicytaty'   => 'Wikicytatami',
1395                'Wikipedia'    => 'Wikipedią',
1396                'Wikipodróże'  => 'Wikipodróżami',
1397                'Wikisłownik'  => 'Wikisłownikiem',
1398                'Wikiźródła'   => 'Wikiźródłami',
1399                'Wikidane'     => 'Wikidanymi',
1400                'Wikimania'    => 'Wikimanią',
1401                'Wikifunkcje'  => 'Wikifunkcjami',
1402            ],
1403            'MS.lp' => [
1404                'Wikicytaty'   => 'Wikicytatach',
1405                'Wikipedia'    => 'Wikipedii',
1406                'Wikipodróże'  => 'Wikipodróżach',
1407                'Wikisłownik'  => 'Wikisłowniku',
1408                'Wikiźródła'   => 'Wikiźródłach',
1409                'Wikidane'     => 'Wikidanych',
1410                'Wikimania'    => 'Wikimanii',
1411                'Wikifunkcje'  => 'Wikifunkcjach',
1412            ],
1413            'W.lp' => [
1414                'Wikipedia'    => 'Wikipedio',
1415                'Wikisłownik'  => 'Wikisłowniku',
1416                'Wikimania'    => 'Wikimanio',
1417            ],
1418        ];
1419
1420        $wgGrammarForms['rmy'] = [
1421            // genitive (m.sg.)
1422            'genitive-m-sg' => [
1423                'Vikipidiya' => 'Vikipidiyako',
1424                'Vikcyonaro' => 'Vikcyonaresko',
1425            ],
1426            // genitive (f.sg.)
1427            'genitive-f-sg' => [
1428                'Vikipidiya' => 'Vikipidiyaki',
1429                'Vikcyonaro' => 'Vikcyonareski',
1430            ],
1431            // genitive (pl.)
1432            'genitive-pl' => [
1433                'Vikipidiya' => 'Vikipidiyake',
1434                'Vikcyonaro' => 'Vikcyonareske',
1435            ],
1436            // dative
1437            'dativ' => [
1438                'Vikipidiya' => 'Wikipediji',
1439                'Vikcyonaro' => 'Vikcyonareske',
1440            ],
1441            // locative
1442            'locative' => [
1443                'Vikipidiya' => 'Wikipedijo',
1444                'Vikcyonaro' => 'Vikcyonareste',
1445            ],
1446            // ablative
1447            'ablative' => [
1448                'Vikipidiya' => 'o Wikipediji',
1449                'Vikcyonaro' => 'Vikcyonarestar',
1450            ],
1451            // instrumental
1452            'instrumental' => [
1453                'Vikipidiya' => 'z Wikipedijo',
1454                'Vikcyonaro' => 'Vikcyonaresa',
1455            ],
1456        ];
1457
1458        $wgGrammarForms['sh'] = [
1459            'genitiv' => [
1460                'Wikiknjige'          => 'Wikiknjiga',
1461                'Wikipodaci'          => 'Wikipodataka',
1462                'Wikifunkcije'        => 'Wikifunkcija',
1463                'Wikimedijina ostava' => 'Wikimedijine ostave',
1464                'Wikipedija'          => 'Wikipedije',
1465                'Wikicitat'           => 'Wikicitata',
1466                'Wikiteka'            => 'Wikiteke',
1467                'Wikivrste'           => 'Wikivrsta',
1468                'Wikiverzitet'        => 'Wikiverziteta',
1469                'Wikivodič'           => 'Wikivodiča',
1470                'Wikirječnik'         => 'Wikirječnika',
1471                'projekt'             => 'projekta',
1472            ],
1473            'dativ' => [
1474                'Wikiknjige'          => 'Wikiknjigama',
1475                'Wikipodaci'          => 'Wikipodacima',
1476                'Wikifunkcije'        => 'Wikifunkcijama',
1477                'Wikimedijina ostava' => 'Wikimedijinoj ostavi',
1478                'Wikinovosti'         => 'Wikinovostima',
1479                'Wikipedija'          => 'Wikipediji',
1480                'Wikicitat'           => 'Wikicitatu',
1481                'Wikiteka'            => 'Wikiteci',
1482                'Wikivrste'           => 'Wikivrstama',
1483                'Wikiverzitet'        => 'Wikiverzitetu',
1484                'Wikivodič'           => 'Wikivodiču',
1485                'Wikirječnik'         => 'Wikirječniku',
1486                'projekt'             => 'projektu',
1487            ],
1488            'akuzativ' => [
1489                'Wikipodaci'          => 'Wikipodatke',
1490                'Wikimedijina ostava' => 'Wikimedijinu ostavu',
1491                'Wikipedija'          => 'Wikipediju',
1492                'Wikiteka'            => 'Wikiteku',
1493                // To support the descriptive declension
1494                'projekt'             => 'projekt',
1495            ],
1496            'vokativ' => [
1497                'Wikimedijina ostava' => 'Wikimedijina ostavo',
1498                'Wikipedija'          => 'Wikipedijo',
1499                'Wikicitat'           => 'Wikicitate',
1500                'Wikiteka'            => 'Wikiteko',
1501                'Wikiverzitet'        => 'Wikiverzitete',
1502                'Wikivodič'           => 'Wikivodiču',
1503                'Wikirječnik'         => 'Wikirječniče',
1504                'projekt'             => 'projekte',
1505            ],
1506            'instrumental' => [
1507                'Wikiknjige'          => 'Wikiknjigama',
1508                'Wikipodaci'          => 'Wikipodacima',
1509                'Wikifunkcije'        => 'Wikifunkcijama',
1510                'Wikimedijina ostava' => 'Wikimedijinom ostavom',
1511                'Wikinovosti'         => 'Wikinovostima',
1512                'Wikipedija'          => 'Wikipedijom',
1513                'Wikicitat'           => 'Wikicitatom',
1514                'Wikiteka'            => 'Wikitekom',
1515                'Wikivrste'           => 'Wikivrstama',
1516                'Wikiverzitet'        => 'Wikiverzitetom',
1517                'Wikivodič'           => 'Wikivodičem',
1518                'Wikirječnik'         => 'Wikirječnikom',
1519                'projekt'             => 'projektom',
1520            ],
1521            'lokativ' => [
1522                'Wikiknjige'          => 'Wikiknjigama',
1523                'Wikipodaci'          => 'Wikipodacima',
1524                'Wikifunkcije'        => 'Wikifunkcijama',
1525                'Wikimedijina ostava' => 'Wikimedijinoj ostavi',
1526                'Wikinovosti'         => 'Wikinovostima',
1527                'Wikipedija'          => 'Wikipediji',
1528                'Wikicitat'           => 'Wikicitatu',
1529                'Wikiteka'            => 'Wikiteci',
1530                'Wikivrste'           => 'Wikivrstama',
1531                'Wikiverzitet'        => 'Wikiverzitetu',
1532                'Wikivodič'           => 'Wikivodiču',
1533                'Wikirječnik'         => 'Wikirječniku',
1534                'projekt'             => 'projektu',
1535            ],
1536        ];
1537
1538        $wgGrammarForms['sk'] = [
1539            'genitív' => [
1540                'Wikipédia'   => 'Wikipédie',
1541                'Wikislovník' => 'Wikislovníku',
1542                'Wikicitáty'  => 'Wikicitátov',
1543                'Wikiknihy'   => 'Wikikníh',
1544            ],
1545            'datív' => [
1546                'Wikipédia'   => 'Wikipédii',
1547                'Wikislovník' => 'Wikislovníku',
1548                'Wikicitáty'  => 'Wikicitátom',
1549                'Wikiknihy'   => 'Wikiknihám',
1550            ],
1551            'akuzatív' => [
1552                'Wikipédia'   => 'Wikipédiu',
1553                'Wikislovník' => 'Wikislovník',
1554                'Wikicitáty'  => 'Wikicitáty',
1555                'Wikiknihy'   => 'Wikiknihy',
1556            ],
1557            'lokál' => [
1558                'Wikipédia'   => 'Wikipédii',
1559                'Wikislovník' => 'Wikislovníku',
1560                'Wikicitáty'  => 'Wikicitátoch',
1561                'Wikiknihy'   => 'Wikiknihách',
1562            ],
1563            'inštrumentál' => [
1564                'Wikipédia'   => 'Wikipédiou',
1565                'Wikislovník' => 'Wikislovníkom',
1566                'Wikicitáty'  => 'Wikicitátmi',
1567                'Wikiknihy'   => 'Wikiknihami',
1568            ],
1569        ];
1570
1571        $wgGrammarForms['sl'] = [
1572            // genitive
1573            'rodilnik' => [
1574                'Wikipedija'  => 'Wikipedije',
1575                'Wikiknjige'  => 'Wikiknjig',
1576                'Wikinovice'  => 'Wikinovic',
1577                'Wikinavedek' => 'Wikinavedka',
1578                'Wikivir'     => 'Wikivira',
1579                'Wikislovar'  => 'Wikislovarja',
1580            ],
1581            // dative
1582            'dajalnik' => [
1583                'Wikipedija'  => 'Wikipediji',
1584                'Wikiknjige'  => 'Wikiknjigam',
1585                'Wikinovice'  => 'Wikinovicam',
1586                'Wikinavedek' => 'Wikinavedku',
1587                'Wikivir'     => 'Wikiviru',
1588                'Wikislovar'  => 'Wikislovarju',
1589            ],
1590            // accusative
1591            'tožilnik' => [
1592                'Wikipedija'  => 'Wikipedijo',
1593                // no need to transform the others
1594            ],
1595            // locative
1596            'mestnik' => [
1597                'Wikipedija'  => 'o Wikipediji',
1598                'Wikiknjige'  => 'o Wikiknjigah',
1599                'Wikinovice'  => 'o Wikinovicah',
1600                'Wikinavedek' => 'o Wikinavedku',
1601                'Wikivir'     => 'o Wikiviru',
1602                'Wikislovar'  => 'o Wikislovarju',
1603            ],
1604            // instrumental
1605            'orodnik' => [
1606                'Wikipedija'  => 'z Wikipedijo',
1607                'Wikiknjige'  => 'z Wikiknjigami',
1608                'Wikinovice'  => 'z Wikinovicami',
1609                'Wikinavedek' => 'z Wikinavedkom',
1610                'Wikivir'     => 'z Wikivirom',
1611                'Wikislovar'  => 'z Wikislovarjem',
1612            ],
1613        ];
1614
1615        $wgGrammarForms['sr-ec'] =
1616        $wgGrammarForms['sr-cyrl'] = [
1617            // genitive
1618            'генитив' => [
1619                'Викиречник'  => 'Викиречника',
1620                'Викицитат'   => 'Викицитата',
1621                'Викизворник' => 'Викизворника',
1622                'Викикњиге'   => 'Викикњига',
1623                'Википедија'  => 'Википедије',
1624                'Википодаци'  => 'Википодатака',
1625                'Викимедијина остава' => 'Викимедијине оставе',
1626                'Викиверзитет' => 'Викиверзитета',
1627            ],
1628            // dative
1629            'датив' => [
1630                'Викиречник'  => 'Викиречнику',
1631                'Викицитат'   => 'Викицитату',
1632                'Викизворник' => 'Викизворнику',
1633                'Викикњиге'   => 'Викикњигама',
1634                'Википедија'  => 'Википедији',
1635                'Википодаци'  => 'Википодацима',
1636                'Викиновости'   => 'Викиновистима',
1637                'Викимедијина остава' => 'Викимедијиној остави',
1638                'Викиверзитет' => 'Викиверзитету',
1639            ],
1640            // accusative
1641            'акузатив' => [
1642                'Викиречник'  => 'Викиречник',
1643                'Викицитат'   => 'Викицитат',
1644                'Викизворник' => 'Викизворник',
1645                'Википедија'  => 'Википедију',
1646                'Википодаци'  => 'Википодатке',
1647            ],
1648            // vocative
1649            'вокатив' => [
1650                'Викиречник'  => 'Викиречниче',
1651                'Викизворник' => 'Викизворниче',
1652                'Википедија'  => 'Википедијо',
1653                'Викимедијина остава' => 'Викимедијина оставо',
1654                'Викиверзитет' => 'Викиверзитету',
1655            ],
1656            // instrumental
1657            'инструментал' => [
1658                'Викиречник'  => 'Викиречником',
1659                'Викицитат'   => 'Викицитатом',
1660                'Викизворник' => 'Викизворником',
1661                'Викикњиге'   => 'Викикњигама',
1662                'Википедија'  => 'Википедијом',
1663                'Википодаци'  => 'Википодацима',
1664                'Викиновости'   => 'Викиновостима',
1665                'Викимедијина остава' => 'Викимедијином оставом',
1666                'Викиверзитет' => 'Викиверзитетом',
1667            ],
1668            // locative
1669            'локатив' => [
1670                'Викиречник'  => 'Викиречнику',
1671                'Викицитат'   => 'Викицитату',
1672                'Викизворник' => 'Викизворнику',
1673                'Викикњиге'   => 'Викикњигама',
1674                'Википедија'  => 'Википедији',
1675                'Википодаци'  => 'Википодацима',
1676                'Викиновости'   => 'Викиновостима',
1677                'Викимедијина остава' => 'Викимедијиној остави',
1678                'Викиверзитет' => 'Викиверзитету',
1679            ],
1680        ];
1681
1682        $wgGrammarForms['sr-el'] =
1683        $wgGrammarForms['sr-latn'] = [
1684            // genitive
1685            'genitiv' => [
1686                'Vikirečnik'  => 'Vikirečnika',
1687                'Vikicitat'   => 'Vikicitata',
1688                'Vikizvornik' => 'Vikizvornika',
1689                'Vikiknjige'  => 'Vikiknjiga',
1690                'Vikipedija'  => 'Vikipedije',
1691                'Vikipodaci'  => 'Vikipodataka',
1692                'Vikimedijina ostava' => 'Vikimedijine ostave',
1693                'Vikiverzitet' => 'Vikiverziteta',
1694            ],
1695            // dative
1696            'dativ' => [
1697                'Vikirečnik'  => 'Vikirečniku',
1698                'Vikicitat'   => 'Vikicitatu',
1699                'Vikizvornik' => 'Vikizvorniku',
1700                'Vikiknjige'  => 'Vikiknjigama',
1701                'Vikipedija'  => 'Vikipediji',
1702                'Vikipodaci'  => 'Vikipodacima',
1703                'Vikinovosti'   => 'Vikinovostima',
1704                'Vikimedijina ostava' => 'Vikimedijinoj ostavi',
1705                'Vikiverzitet' => 'Vikiverzitetu',
1706            ],
1707            // accusative
1708            'akuzativ' => [
1709                'Vikirečnik'  => 'Vikirečnik',
1710                'Vikicitat'   => 'Vikicitat',
1711                'Vikizvornik' => 'Vikizvornik',
1712                'Vikipedija'  => 'Vikipediju',
1713                'Vikipodaci'  => 'Vikipodatke',
1714            ],
1715            // vocative
1716            'vokativ' => [
1717                'Vikirečnik'  => 'Vikirečniče',
1718                'Vikizvornik' => 'Vikizvorniče',
1719                'Vikipedija'  => 'Vikipedijo',
1720                'Vikimedijina ostava' => 'Vikimedijina ostavo',
1721                'Vikiverzitet' => 'Vikiverzitetu',
1722            ],
1723            // instrumental
1724            'instrumental' => [
1725                'Vikirečnik'  => 'Vikirečnikom',
1726                'Vikicitat'   => 'Vikicitatom',
1727                'Vikizvornik' => 'Vikizvornikom',
1728                'Vikiknjige'  => 'Vikiknjigama',
1729                'Vikipedija'  => 'Vikipedijom',
1730                'Vikipodaci'  => 'Vikipodacima',
1731                'Vikinovosti'   => 'Vikinovostima',
1732                'Vikimedijina ostava' => 'Vikimedijinom ostavom',
1733                'Vikiverzitet' => 'Vikiverzitetom',
1734            ],
1735            // locative
1736            'lokativ' => [
1737                'Vikirečnik'  => 'Vikirečniku',
1738                'Vikicitat'   => 'Vikicitatu',
1739                'Vikizvornik' => 'Vikizvorniku',
1740                'Vikiknjige'  => 'Vikiknjigama',
1741                'Vikipedija'  => 'Vikipediji',
1742                'Vikipodaci'  => 'Vikipodacima',
1743                'Vikinovosti'   => 'Vikinovostima',
1744                'Vikimedijina ostava' => 'Vikimedijinoj ostavi',
1745                'Vikiverzitet' => 'Vikiverzitetu',
1746            ],
1747        ];
1748
1749        $wgGrammarForms['uk'] = [
1750            // genitive
1751            'genitive' => [
1752                'Вікіпедія' => 'Вікіпедії',
1753                'Вікісловник' => 'Вікісловника',
1754                'Вікісховище' => 'Вікісховища',
1755                'Вікіпідручник' => 'Вікіпідручника',
1756                'Вікіцитати' => 'Вікіцитат',
1757                'Вікіджерела' => 'Вікіджерел',
1758                'Вікіновини' => 'Вікіновин',
1759                'Вікідані' => 'Вікіданих',
1760                'Вікімандри' => 'Вікімандрів',
1761            ],
1762            // dative
1763            'dative' => [
1764                'Вікіпедія' => 'Вікіпедії',
1765                'Вікісловник' => 'Вікісловнику',
1766                'Вікісховище' => 'Вікісховищу',
1767                'Вікіпідручник' => 'Вікіпідручнику',
1768                'Вікіцитати' => 'Вікіцитатам',
1769                'Вікіджерела' => 'Вікіджерелам',
1770                'Вікіновини' => 'Вікіновинам',
1771                'Вікідані' => 'Вікіданим',
1772                'Вікімандри' => 'Вікімандрам',
1773            ],
1774            // accusative
1775            'accusative' => [
1776                'Вікіпедія' => 'Вікіпедію',
1777                'Вікісловник' => 'Вікісловник',
1778                'Вікісховище' => 'Вікісховище',
1779                'Вікіпідручник' => 'Вікіпідручник',
1780                'Вікіцитати' => 'Вікіцитати',
1781                'Вікіджерела' => 'Вікіджерела',
1782                'Вікіновини' => 'Вікіновини',
1783                'Вікідані' => 'Вікідані',
1784                'Вікімандри' => 'Вікімандри',
1785            ],
1786            // instrumental
1787            'instrumental' => [
1788                'Вікіпедія' => 'Вікіпедією',
1789                'Вікісловник' => 'Вікісловником',
1790                'Вікісховище' => 'Вікісховищем',
1791                'Вікіпідручник' => 'Вікіпідручником',
1792                'Вікіцитати' => 'Вікіцитатами',
1793                'Вікіджерела' => 'Вікіджерелами',
1794                'Вікіновини' => 'Вікіновинами',
1795                'Вікідані' => 'Вікіданими',
1796                'Вікімандри' => 'Вікімандрами',
1797            ],
1798            // locative
1799            'locative' => [
1800                'Вікіпедія' => 'у Вікіпедії',
1801                'Вікісловник' => 'у Вікісловнику',
1802                'Вікісховище' => 'у Вікісховищі',
1803                'Вікіпідручник' => 'у Вікіпідручнику',
1804                'Вікіцитати' => 'у Вікіцитатах',
1805                'Вікіджерела' => 'у Вікіджерелах',
1806                'Вікіновини' => 'у Вікіновинах',
1807                'Вікідані' => 'у Вікіданих',
1808                'Вікімандри' => 'у Вікімандрах',
1809            ],
1810            // vocative
1811            'vocative' => [
1812                'Вікіпедія' => 'Вікіпедіє',
1813                'Вікісловник' => 'Вікісловнику',
1814                'Вікісховище' => 'Вікісховище',
1815                'Вікіпідручник' => 'Вікіпідручнику',
1816                'Вікіцитати' => 'Вікіцитати',
1817                'Вікіджерела' => 'Вікіджерела',
1818                'Вікіновини' => 'Вікіновини',
1819                'Вікідані' => 'Вікідані',
1820                'Вікімандри' => 'Вікімандри',
1821            ],
1822        ];
1823
1824        // Avoid weirdness if both extensions are loaded at the same time.
1825        if ( !ExtensionRegistry::getInstance()->isLoaded( 'UserMerge' ) ) {
1826            global $wgLogTypes, $wgLogNames, $wgLogHeaders, $wgLogActionsHandlers, $wgActionFilteredLogs;
1827
1828            $wgLogTypes[] = 'usermerge';
1829            $wgLogNames['usermerge'] = 'wikimedia-usermerge-logpage';
1830            $wgLogHeaders['usermerge'] = 'wikimedia-usermerge-logpagetext';
1831
1832            $wgLogActionsHandlers['usermerge/*'] = WMUserMergeLogFormatter::class;
1833            $wgActionFilteredLogs['usermerge'] = [
1834                'mergeuser'  => [ 'mergeuser' ],
1835                'deleteuser' => [ 'deleteuser' ],
1836            ];
1837        }
1838
1839        global $wgWikimediaMessagesHasLiquidThreadsLogs;
1840        if (
1841            !ExtensionRegistry::getInstance()->isLoaded( 'Liquid Threads' ) &&
1842            $wgWikimediaMessagesHasLiquidThreadsLogs
1843        ) {
1844            global $wgLogTypes, $wgLogNames, $wgLogHeaders, $wgLogActionsHandlers;
1845
1846            $wgLogTypes[] = 'liquidthreads';
1847            $wgLogNames['liquidthreads'] = 'wikimedia-lqt-log-name';
1848            $wgLogHeaders['liquidthreads'] = 'wikimedia-lqt-log-header';
1849
1850            $wgLogActionsHandlers['liquidthreads/*'] = WMLiquidThreadsLogFormatter::class;
1851        }
1852    }
1853
1854    /**
1855     * Called every time wikitext is added to the OutputPage, after it is parsed but before it is added.
1856     *
1857     * @param OutputPage $out The Output page object
1858     * @param string &$text Text that will be displayed, in HTML
1859     */
1860    public function onOutputPageBeforeHTML( $out, &$text ): void {
1861        $skins = $out->getConfig()->get( 'WikimediaStylesSkins' );
1862
1863        if ( in_array( $out->getSkin()->getSkinName(), $skins ) ) {
1864            $out->addModuleStyles( [ 'ext.wikimediamessages.styles' ] );
1865        }
1866    }
1867
1868    /**
1869     * @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderRegisterModules
1870     *
1871     * @param ResourceLoader $resourceLoader
1872     */
1873    public function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ): void {
1874        if ( $this->extensionRegistry->isLoaded( 'IPInfo' ) ) {
1875            $resourceLoader->register( 'ext.wikimediaMessages.ipInfo.hooks', [
1876                'localBasePath' => dirname( __DIR__ ) . '/modules/ext.wikimediaMessages.ipInfo.hooks',
1877                'remoteExtPath' => 'WikimediaMessages/modules/ext.wikimediaMessages.ipInfo.hooks',
1878                'scripts' => 'infobox.js',
1879                'styles' => 'infobox.less',
1880                'messages' => [
1881                    'ipinfo-global-contributions-url-label',
1882                    'ipinfo-global-contributions-url',
1883                    'ipinfo-xtools-url-label',
1884                    'ipinfo-xtools-url',
1885                ],
1886                'dependencies' => [
1887                    'ext.ipInfo',
1888                ],
1889            ] );
1890        }
1891    }
1892
1893    /**
1894     * Handle SpecialPageBeforeExecute hook
1895     *
1896     * @param SpecialPage $special
1897     * @param string|null $subPage
1898     */
1899    public function onSpecialPageBeforeExecute( $special, $subPage ) {
1900        $this->addIPInfoLinks( $special, $subPage );
1901    }
1902
1903    /**
1904     * @param SpecialPage $special
1905     * @param string|null $subPage
1906     */
1907    private function addIPInfoLinks( $special, $subPage ): void {
1908        if (
1909            $special->getName() !== 'Contributions' &&
1910            $special->getName() !== 'DeletedContributions'
1911        ) {
1912            return;
1913        }
1914
1915        // Return if target is not an IP address
1916        if ( $subPage === null || !IPUtils::isValid( $subPage ) ) {
1917            return;
1918        }
1919
1920        $accessingUser = $special->getUser();
1921        $isBetaFeaturesLoaded = $this->extensionRegistry->isLoaded( 'BetaFeatures' );
1922
1923        // Check the same permissions and preferences as
1924        // MediaWiki\IPInfo\HookHandler\InfoboxHandler
1925        if (
1926            !$this->permissionManager->userHasRight( $accessingUser, 'ipinfo' ) ||
1927            ( $isBetaFeaturesLoaded &&
1928                !$this->userOptionsLookup->getOption( $accessingUser, 'ipinfo-beta-feature-enable' ) )
1929        ) {
1930            return;
1931        }
1932
1933        $special->getOutput()->addModules( 'ext.wikimediaMessages.ipInfo.hooks' );
1934    }
1935
1936    /**
1937     * Whether or not the donate link should be moved from the sidebar to the user menu
1938     *
1939     * @param Skin $skin
1940     * @return bool
1941     */
1942    public function shouldMoveDonateLink( $skin ): bool {
1943        $config = $skin->getConfig();
1944        $user = $skin->getUser();
1945
1946        if (
1947            $skin->getSkinName() === 'vector-2022' &&
1948            $config->get( 'WikimediaMessagesAnonDonateLink' ) &&
1949            $user->isAnon()
1950        ) {
1951            return true;
1952        }
1953        return false;
1954    }
1955
1956    /**
1957     * Add a donate link to the user links menu for anonymous users on vector '22, if feature flag is turned on
1958     *
1959     * @param SkinTemplate $skin
1960     * @param array &$links
1961     */
1962    public function onSkinTemplateNavigation__Universal( $skin, &$links ): void {
1963        $context = $skin->getContext();
1964
1965        if ( $this->shouldMoveDonateLink( $skin ) ) {
1966            $sitesupport = [ 'sitesupport' => [
1967                'text' => $context->msg( 'sitesupport' )->text(),
1968                'href' => $context->msg( 'sitesupport-url' )->text(),
1969                'title' => $context->msg( 'tooltip-n-sitesupport' )->text(),
1970                'icon' => 'heart',
1971            ] ];
1972            // Ensure donate link goes before other links
1973            if ( array_key_exists( 'user-menu', $links ) ) {
1974                $links['user-menu'] = $sitesupport + $links['user-menu'];
1975            }
1976        }
1977    }
1978
1979    /**
1980     * Remove the donate link for anonymous users on vector '22, if the feature flag is turned on
1981     *
1982     * @param Skin $skin
1983     * @param array &$sidebar
1984     */
1985    public function onSidebarBeforeOutput( $skin, &$sidebar ): void {
1986        // we want to be sure we're only removing this link if it's going to appear elsewhere
1987        if ( $this->shouldMoveDonateLink( $skin ) ) {
1988            // the donate link is not guaranteed to be in a particular section, so we must traverse them all
1989            foreach ( $sidebar as $section => $links ) {
1990                // every other array length is bounded by practicality, but skip looping over language for peformance
1991                if ( $section === 'LANGUAGES' ) {
1992                    continue;
1993                }
1994
1995                foreach ( $links as $index => $link ) {
1996                    if ( isset( $link['id'] ) && $link['id'] === 'n-sitesupport' ) {
1997                        unset( $sidebar[$section][$index] );
1998                    }
1999                }
2000            }
2001        }
2002    }
2003}