Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 97
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
PreferenceHooks
0.00% covered (danger)
0.00%
0 / 97
0.00% covered (danger)
0.00%
0 / 3
552
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
 arrayRenameKey
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 onGetPreferences
0.00% covered (danger)
0.00%
0 / 89
0.00% covered (danger)
0.00%
0 / 1
380
1<?php
2/**
3 * DiscussionTools preference hooks
4 *
5 * @file
6 * @ingroup Extensions
7 * @license MIT
8 */
9
10namespace MediaWiki\Extension\DiscussionTools\Hooks;
11
12use MediaWiki\Config\Config;
13use MediaWiki\Config\ConfigFactory;
14use MediaWiki\Html\Html;
15use MediaWiki\Linker\LinkRenderer;
16use MediaWiki\Preferences\Hook\GetPreferencesHook;
17use MediaWiki\SpecialPage\SpecialPage;
18use MediaWiki\User\User;
19
20class PreferenceHooks implements
21    GetPreferencesHook
22{
23
24    private Config $config;
25    private LinkRenderer $linkRenderer;
26
27    public function __construct(
28        ConfigFactory $configFactory,
29        LinkRenderer $linkRenderer
30    ) {
31        $this->config = $configFactory->makeConfig( 'discussiontools' );
32        $this->linkRenderer = $linkRenderer;
33    }
34
35    /**
36     * Rename a key in an array while preserving the order of associative array keys.
37     *
38     * @param array $array
39     * @param string $from
40     * @param string $to
41     * @return array Modified array
42     */
43    private static function arrayRenameKey( array $array, string $from, string $to ): array {
44        $out = [];
45        foreach ( $array as $key => $value ) {
46            if ( $key === $from ) {
47                $key = $to;
48            }
49            $out[$key] = $value;
50        }
51        return $out;
52    }
53
54    /**
55     * Handler for the GetPreferences hook, to add and hide user preferences as configured
56     *
57     * @param User $user
58     * @param array &$preferences
59     */
60    public function onGetPreferences( $user, &$preferences ) {
61        if ( HookUtils::isFeatureAvailableToUser( $user ) ) {
62            $preferences['discussiontools-summary'] = [
63                'type' => 'info',
64                'default' => wfMessage( 'discussiontools-preference-summary' )->parse(),
65                'raw' => true,
66                'section' => 'editing/discussion',
67            ];
68        }
69        foreach ( HookUtils::FEATURES as $feature ) {
70            if (
71                $feature === HookUtils::VISUALENHANCEMENTS_REPLY ||
72                $feature === HookUtils::VISUALENHANCEMENTS_PAGEFRAME
73            ) {
74                // Feature is never user-configurable
75                continue;
76            }
77            if ( HookUtils::isFeatureAvailableToUser( $user, $feature ) ) {
78                $preferences["discussiontools-$feature"] = [
79                    'type' => 'toggle',
80                    // The following messages are used here:
81                    // * discussiontools-preference-autotopicsub
82                    // * discussiontools-preference-newtopictool
83                    // * discussiontools-preference-replytool
84                    // * discussiontools-preference-sourcemodetoolbar
85                    // * discussiontools-preference-topicsubscription
86                    // * discussiontools-preference-visualenhancements
87                    'label-message' => "discussiontools-preference-$feature",
88                    // The following messages are used here:
89                    // * discussiontools-preference-autotopicsub-help
90                    // * discussiontools-preference-newtopictool-help
91                    // * discussiontools-preference-replytool-help
92                    // * discussiontools-preference-sourcemodetoolbar-help
93                    // * discussiontools-preference-topicsubscription-help
94                    // * discussiontools-preference-visualenhancements-help
95                    'help-message' => "discussiontools-preference-$feature-help",
96                    'section' => 'editing/discussion',
97                ];
98
99                // Option to enable/disable new topic tool on pages that haven't been created
100                // (it's inside this loop to place the options in a nice order)
101                if ( $feature === HookUtils::NEWTOPICTOOL ) {
102                    $preferences["discussiontools-newtopictool-createpage"] = [
103                        'type' => 'radio',
104                        'cssclass' => 'mw-htmlform-checkradio-indent',
105                        'label-message' => 'discussiontools-preference-newtopictool-createpage',
106                        'options-messages' => [
107                            'discussiontools-preference-newtopictool-createpage-newtopictool' => 1,
108                            'discussiontools-preference-newtopictool-createpage-editor' => 0,
109                        ],
110                        'disable-if' => [ '===', 'discussiontools-' . HookUtils::NEWTOPICTOOL, '' ],
111                        'section' => 'editing/discussion',
112                    ];
113                }
114
115                // Make this option unavailable when a conflicting Convenient Discussions gadget exists
116                // (we can't use 'disable-if' or 'hide-if', because they don't let us change the labels).
117                if ( HookUtils::featureConflictsWithGadget( $user, $feature ) ) {
118                    $preferences["discussiontools-$feature"]['disabled'] = true;
119                    $preferences["discussiontools-$feature"]['help-message'] =
120                        [ 'discussiontools-preference-gadget-conflict', 'Special:Preferences#mw-prefsection-gadgets' ];
121                }
122            }
123        }
124
125        if ( isset( $preferences['discussiontools-' . HookUtils::SOURCEMODETOOLBAR] ) && (
126            isset( $preferences['discussiontools-' . HookUtils::REPLYTOOL] ) ||
127            isset( $preferences['discussiontools-' . HookUtils::NEWTOPICTOOL] )
128        ) ) {
129            // Disable this option when it would have no effect
130            // (both reply tool and new topic tool are disabled)
131            $preferences['discussiontools-' . HookUtils::SOURCEMODETOOLBAR]['disable-if'] = [ 'AND' ];
132
133            if ( isset( $preferences['discussiontools-' . HookUtils::REPLYTOOL] ) &&
134                // GlobalPreferences extension would delete disabled fields, avoid referring to it.
135                !( $preferences['discussiontools-' . HookUtils::REPLYTOOL]['disabled'] ?? false )
136            ) {
137                $preferences['discussiontools-' . HookUtils::SOURCEMODETOOLBAR]['disable-if'][] = [
138                    '===', 'discussiontools-' . HookUtils::REPLYTOOL, ''
139                ];
140            }
141            if ( isset( $preferences['discussiontools-' . HookUtils::NEWTOPICTOOL] ) ) {
142                $preferences['discussiontools-' . HookUtils::SOURCEMODETOOLBAR]['disable-if'][] = [
143                    '===', 'discussiontools-' . HookUtils::NEWTOPICTOOL, ''
144                ];
145            }
146        }
147
148        if ( isset( $preferences['discussiontools-' . HookUtils::AUTOTOPICSUB] ) &&
149            isset( $preferences['discussiontools-' . HookUtils::TOPICSUBSCRIPTION] )
150        ) {
151            // Disable automatic subscriptions when subscriptions are disabled
152            $preferences['discussiontools-' . HookUtils::AUTOTOPICSUB]['disable-if'] = [
153                '===', 'discussiontools-' . HookUtils::TOPICSUBSCRIPTION, ''
154            ];
155        }
156
157        $preferences['discussiontools-showadvanced'] = [
158            'type' => 'api',
159        ];
160        $preferences['discussiontools-seenautotopicsubpopup'] = [
161            'type' => 'api',
162        ];
163
164        if ( !$this->config->get( 'DiscussionToolsBeta' ) ) {
165            // When out of beta, preserve the user preference in case we
166            // bring back the beta feature for a new sub-feature. (T272071)
167            $preferences['discussiontools-betaenable'] = [
168                'type' => 'api'
169            ];
170        }
171
172        $preferences['discussiontools-editmode'] = [
173            'type' => 'api',
174            'validation-callback' => static function ( $value ) {
175                return in_array( $value, [ '', 'source', 'visual' ], true );
176            },
177        ];
178
179        // Add a link to Special:TopicSubscriptions to the Echo preferences matrix
180        $categoryMessage = wfMessage( 'echo-category-title-dt-subscription' )->numParams( 1 )->escaped();
181        $categoryMessageExtra = $categoryMessage .
182            Html::element( 'br' ) .
183            wfMessage( 'parentheses' )->rawParams(
184                $this->linkRenderer->makeLink(
185                    SpecialPage::getTitleFor( 'TopicSubscriptions' ),
186                    wfMessage( 'discussiontools-topicsubscription-preferences-editsubscriptions' )->text()
187                )
188            )->escaped();
189        if ( isset( $preferences['echo-subscriptions']['rows'] ) ) {
190            $preferences['echo-subscriptions']['rows'] = static::arrayRenameKey(
191                $preferences['echo-subscriptions']['rows'],
192                $categoryMessage,
193                $categoryMessageExtra
194            );
195        }
196        if ( isset( $preferences['echo-subscriptions']['tooltips'] ) ) {
197            $preferences['echo-subscriptions']['tooltips'] = static::arrayRenameKey(
198                // Phan insists that this key doesn't exist, even though we just checked with isset()
199                // @phan-suppress-next-line PhanTypeInvalidDimOffset, PhanTypeMismatchArgument
200                $preferences['echo-subscriptions']['tooltips'],
201                $categoryMessage,
202                $categoryMessageExtra
203            );
204        }
205    }
206
207}