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