Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hooks
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 5
156
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 onBeforePageDisplay
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 onSaveUserOptions
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 onPreferencesFormPreSave
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 localPreferencesFormPreSave
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3namespace GlobalPreferences;
4
5use MediaWiki\HTMLForm\HTMLForm;
6use MediaWiki\Output\Hook\BeforePageDisplayHook;
7use MediaWiki\Output\OutputPage;
8use MediaWiki\Preferences\Hook\PreferencesFormPreSaveHook;
9use MediaWiki\Preferences\PreferencesFactory;
10use MediaWiki\Skin\Skin;
11use MediaWiki\User\Options\Hook\SaveUserOptionsHook;
12use MediaWiki\User\Options\UserOptionsLookup;
13use MediaWiki\User\Options\UserOptionsManager;
14use MediaWiki\User\User;
15use MediaWiki\User\UserIdentity;
16
17class Hooks implements
18    BeforePageDisplayHook,
19    SaveUserOptionsHook,
20    PreferencesFormPreSaveHook
21    {
22
23    /** @var GlobalPreferencesFactory */
24    private $preferencesFactory;
25
26    /** @var UserOptionsManager */
27    private $userOptionsManager;
28
29    /**
30     * @param PreferencesFactory $preferencesFactory
31     * @param UserOptionsManager $userOptionsManager
32     */
33    public function __construct(
34        PreferencesFactory $preferencesFactory,
35        UserOptionsManager $userOptionsManager
36    ) {
37        $this->preferencesFactory = $preferencesFactory;
38        $this->userOptionsManager = $userOptionsManager;
39    }
40
41    /**
42     * Allows last minute changes to the output page, e.g. adding of CSS or JavaScript by extensions.
43     * @link https://www.mediawiki.org/wiki/Manual:Hooks/BeforePageDisplay
44     * @param OutputPage $out The output page.
45     * @param Skin $skin The skin. Not used.
46     */
47    public function onBeforePageDisplay( $out, $skin ): void {
48        if ( $out->getTitle()->isSpecial( 'Preferences' ) ) {
49            // Add module styles and scripts separately
50            // so non-JS users get the styles quicker and to avoid a FOUC.
51            $out->addModuleStyles( 'ext.GlobalPreferences.local-nojs' );
52            $out->addModules( 'ext.GlobalPreferences.local' );
53        }
54    }
55
56    /**
57     * When saving a user's options, remove any global ones and never save any on the Global
58     * Preferences page. Global options are saved separately, in the PreferencesFormPreSave hook.
59     *
60     * @see https://www.mediawiki.org/wiki/Manual:Hooks/SaveUserOptions
61     * @param UserIdentity $user The user.
62     * @param string[] &$modifiedOptions The user's options that were modified.
63     * @param string[] $originalOptions The original options.
64     * @return bool False if nothing changed, true otherwise.
65     */
66    public function onSaveUserOptions( UserIdentity $user, array &$modifiedOptions, array $originalOptions ) {
67        if ( $this->preferencesFactory->onGlobalPrefsPage() ) {
68            // It shouldn't be possible to save local options here,
69            // but never save on this page anyways.
70            return false;
71        }
72
73        $this->preferencesFactory->handleLocalPreferencesChange( $user, $modifiedOptions, $originalOptions );
74
75        return true;
76    }
77
78    /**
79     * @link https://www.mediawiki.org/wiki/Manual:Hooks/PreferencesFormPreSave
80     * @param array $formData An associative array containing the data from the preferences form.
81     * @param HTMLForm $form The HTMLForm object that represents the preferences form.
82     * @param User $user The User object that can be used to change the user's preferences.
83     * @param bool &$result The boolean return value of the Preferences::tryFormSubmit method.
84     * @param array $oldUserOptions Array with user's old options (before save)
85     * @return bool|void True or no return value to continue or false to abort
86     */
87    public function onPreferencesFormPreSave( $formData, $form, $user, &$result, $oldUserOptions ) {
88        if ( !$this->preferencesFactory->onGlobalPrefsPage( $form ) ) {
89            return $this->localPreferencesFormPreSave( $formData, $user );
90        }
91        return true;
92    }
93
94    /**
95     * Process PreferencesFormPreSave for Special:Preferences
96     * Handles CheckMatrix
97     *
98     * @param array $formData Associative array of [ preference name => value ]
99     * @param User $user Current user
100     * @return bool Hook return value
101     */
102    private function localPreferencesFormPreSave( array $formData, User $user ): bool {
103        foreach ( $formData as $pref => $value ) {
104            if ( !GlobalPreferencesFactory::isLocalPrefName( $pref ) ) {
105                continue;
106            }
107            // Determine the real name of the preference.
108            $suffixLen = strlen( UserOptionsLookup::LOCAL_EXCEPTION_SUFFIX );
109            $realName = substr( $pref, 0, -$suffixLen );
110            if ( isset( $formData[$realName] ) ) {
111                // Not a CheckMatrix field
112                continue;
113            }
114            $checkMatrix = preg_grep( "/^$realName-/", array_keys( $formData ) );
115            foreach ( $checkMatrix as $check ) {
116                $localExceptionName = $check . UserOptionsLookup::LOCAL_EXCEPTION_SUFFIX;
117                $this->userOptionsManager->setOption( $user, $localExceptionName, $value );
118            }
119        }
120        return true;
121    }
122}