Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
47.83% covered (danger)
47.83%
44 / 92
28.57% covered (danger)
28.57%
2 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialPreferences
48.35% covered (danger)
48.35%
44 / 91
28.57% covered (danger)
28.57%
2 / 7
36.28
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 doesWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
67.86% covered (warning)
67.86%
38 / 56
0.00% covered (danger)
0.00%
0 / 1
5.83
 getFormObject
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 showResetForm
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 submitReset
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Specials;
8
9use MediaWiki\Context\IContextSource;
10use MediaWiki\Exception\PermissionsError;
11use MediaWiki\Html\Html;
12use MediaWiki\HTMLForm\HTMLForm;
13use MediaWiki\MediaWikiServices;
14use MediaWiki\Preferences\PreferencesFactory;
15use MediaWiki\SpecialPage\SpecialPage;
16use MediaWiki\Specials\Forms\PreferencesFormOOUI;
17use MediaWiki\User\Options\UserOptionsManager;
18use MediaWiki\User\User;
19use OOUI\FieldLayout;
20use OOUI\SearchInputWidget;
21
22/**
23 * A special page that allows users to change their preferences
24 *
25 * @ingroup SpecialPage
26 */
27class SpecialPreferences extends SpecialPage {
28
29    private PreferencesFactory $preferencesFactory;
30    private UserOptionsManager $userOptionsManager;
31
32    public function __construct(
33        ?PreferencesFactory $preferencesFactory = null,
34        ?UserOptionsManager $userOptionsManager = null
35    ) {
36        parent::__construct( 'Preferences' );
37        // This class is extended and therefore falls back to global state - T265924
38        $services = MediaWikiServices::getInstance();
39        $this->preferencesFactory = $preferencesFactory ?? $services->getPreferencesFactory();
40        $this->userOptionsManager = $userOptionsManager ?? $services->getUserOptionsManager();
41    }
42
43    /** @inheritDoc */
44    public function doesWrites() {
45        return true;
46    }
47
48    /** @inheritDoc */
49    public function execute( $par ) {
50        $this->setHeaders();
51        $this->outputHeader();
52        $out = $this->getOutput();
53        $out->disallowUserJs(); # Prevent hijacked user scripts from sniffing passwords etc.
54
55        $this->requireNamedUser( 'prefsnologintext2' );
56        $this->checkReadOnly();
57
58        if ( $par == 'reset' ) {
59            $this->showResetForm();
60
61            return;
62        }
63
64        $out->addModules( 'mediawiki.special.preferences.ooui' );
65        $out->addModuleStyles( [
66            'mediawiki.special.preferences.styles.ooui',
67            'oojs-ui-widgets.styles',
68        ] );
69
70        $session = $this->getRequest()->getSession();
71        if ( $session->get( 'specialPreferencesSaveSuccess' ) ) {
72            // Remove session data for the success message
73            $session->remove( 'specialPreferencesSaveSuccess' );
74            $out->addModuleStyles( [
75                'mediawiki.codex.messagebox.styles',
76                'mediawiki.notification.convertmessagebox.styles'
77            ] );
78
79            $out->addHTML(
80                Html::successBox(
81                    Html::element(
82                        'p',
83                        [],
84                        $this->msg( 'savedprefs' )->text()
85                    ),
86                    'mw-preferences-messagebox mw-notify-success'
87                )
88            );
89        }
90
91        $this->addHelpLink( 'Help:Preferences' );
92
93        // Load the user from the primary DB to reduce CAS errors on double post (T95839)
94        if ( $this->getRequest()->wasPosted() ) {
95            $user = $this->getUser()->getInstanceFromPrimary() ?? $this->getUser();
96        } else {
97            $user = $this->getUser();
98        }
99
100        $htmlForm = $this->getFormObject( $user, $this->getContext() );
101        $sectionTitles = $htmlForm->getPreferenceSections();
102
103        $prefTabs = [];
104        foreach ( $sectionTitles as $key ) {
105            $prefTabs[] = [
106                'name' => $key,
107                'label' => $htmlForm->getLegend( $key ),
108            ];
109        }
110        $out->addJsConfigVars( 'wgPreferencesTabs', $prefTabs );
111
112        $out->addHTML( ( new FieldLayout(
113            new SearchInputWidget( [
114                'placeholder' => $this->msg( 'searchprefs' )->text(),
115            ] ),
116            [
117                'classes' => [ 'mw-prefs-search' ],
118                'label' => $this->msg( 'searchprefs' )->text(),
119                'invisibleLabel' => true,
120                'infusable' => true,
121            ]
122        ) )->toString() );
123        $htmlForm->show();
124    }
125
126    /**
127     * Get the preferences form to use.
128     * @param User $user
129     * @param IContextSource $context
130     * @return PreferencesFormOOUI|HTMLForm
131     */
132    protected function getFormObject( $user, IContextSource $context ) {
133        $form = $this->preferencesFactory->getForm( $user, $context, PreferencesFormOOUI::class );
134        return $form;
135    }
136
137    protected function showResetForm() {
138        if ( !$this->getAuthority()->isAllowed( 'editmyoptions' ) ) {
139            throw new PermissionsError( 'editmyoptions' );
140        }
141
142        $this->getOutput()->addWikiMsg( 'prefs-reset-intro' );
143
144        $desc = [
145            'confirm' => [
146                'type' => 'check',
147                'label-message' => 'prefs-reset-confirm',
148                'required' => true,
149            ],
150        ];
151        // TODO: disable the submit button if the checkbox is not checked
152        HTMLForm::factory( 'ooui', $desc, $this->getContext(), 'prefs-restore' )
153            ->setTitle( $this->getPageTitle( 'reset' ) ) // Reset subpage
154            ->setSubmitTextMsg( 'restoreprefs' )
155            ->setSubmitDestructive()
156            ->setSubmitCallback( $this->submitReset( ... ) )
157            ->showCancel()
158            ->setCancelTarget( $this->getPageTitle() )
159            ->show();
160    }
161
162    /**
163     * @param array $formData
164     * @return bool
165     */
166    public function submitReset( $formData ) {
167        if ( !$this->getAuthority()->isAllowed( 'editmyoptions' ) ) {
168            throw new PermissionsError( 'editmyoptions' );
169        }
170
171        $user = $this->getUser();
172        $this->userOptionsManager->resetAllOptions( $user );
173        $this->userOptionsManager->saveOptions( $user );
174
175        // Set session data for the success message
176        $this->getRequest()->getSession()->set( 'specialPreferencesSaveSuccess', 1 );
177
178        $url = $this->getPageTitle()->getFullUrlForRedirect();
179        $this->getOutput()->redirect( $url );
180
181        return true;
182    }
183
184    /** @inheritDoc */
185    protected function getGroupName() {
186        return 'login';
187    }
188}
189
190/**
191 * Retain the old class name for backwards compatibility.
192 * @deprecated since 1.41
193 */
194class_alias( SpecialPreferences::class, 'SpecialPreferences' );