Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
27 / 27 |
|
100.00% |
4 / 4 |
CRAP | |
100.00% |
1 / 1 |
SettingsManager | |
100.00% |
27 / 27 |
|
100.00% |
4 / 4 |
5 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
saveUserOptions | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
logSettingsChange | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
saveOptionsInternal | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | /** |
4 | * Backend for setting (and eventually retrieving) user settings |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License along |
17 | * with this program; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | * http://www.gnu.org/copyleft/gpl.html |
20 | * |
21 | * @file |
22 | */ |
23 | |
24 | namespace MediaWiki\Extension\GlobalWatchlist; |
25 | |
26 | use FormatJson; |
27 | use IBufferingStatsdDataFactory; |
28 | use MediaWiki\User\Options\UserOptionsManager; |
29 | use MediaWiki\User\UserIdentity; |
30 | use Psr\Log\LoggerInterface; |
31 | |
32 | /** |
33 | * @author DannyS712 |
34 | * @internal |
35 | */ |
36 | class SettingsManager { |
37 | |
38 | /** |
39 | * Name for the user option in the database with the user's global watchlist settings |
40 | * @note This must be the same as the options name used in getSettings.js |
41 | */ |
42 | public const PREFERENCE_NAME = 'global-watchlist-options'; |
43 | |
44 | /** |
45 | * Latest version of the format the settings are in, in case it changes |
46 | */ |
47 | public const PREFERENCE_VERSION = 1; |
48 | |
49 | /** |
50 | * Make the code clearer by using constants instead of 0, 1, or 2 to represent the filter |
51 | * statuses. Used for anon filter, bot filter, and minor filter |
52 | */ |
53 | |
54 | /** Don't care, not filtered */ |
55 | public const FILTER_EITHER = 0; |
56 | /** Require that the condition (anon/bot/minor) be matched */ |
57 | public const FILTER_REQUIRE = 1; |
58 | /** Exclude edits that match the condition */ |
59 | public const FILTER_EXCLUDE = 2; |
60 | |
61 | /** @var LoggerInterface */ |
62 | private $logger; |
63 | |
64 | /** @var UserOptionsManager */ |
65 | private $userOptionsManager; |
66 | |
67 | /** @var IBufferingStatsdDataFactory */ |
68 | private $statsdDataFactory; |
69 | |
70 | /** |
71 | * @param LoggerInterface $logger |
72 | * @param UserOptionsManager $userOptionsManager |
73 | * @param IBufferingStatsdDataFactory $statsdDataFactory |
74 | */ |
75 | public function __construct( |
76 | LoggerInterface $logger, |
77 | UserOptionsManager $userOptionsManager, |
78 | IBufferingStatsdDataFactory $statsdDataFactory |
79 | ) { |
80 | $this->logger = $logger; |
81 | $this->userOptionsManager = $userOptionsManager; |
82 | $this->statsdDataFactory = $statsdDataFactory; |
83 | } |
84 | |
85 | /** |
86 | * Save user settings |
87 | * |
88 | * @param UserIdentity $userIdentity |
89 | * @param array $options |
90 | */ |
91 | public function saveUserOptions( UserIdentity $userIdentity, array $options ) { |
92 | $this->logSettingsChange( $userIdentity ); |
93 | |
94 | // When saving the options, make sure that `version` is at the start of the |
95 | // serialized JSON. In case we ever need to make a breaking change to the |
96 | // preferences and use a maintenance script to migrate existing settings, |
97 | // we will need to be able to search for users that have the old version |
98 | // number. Its a lot quicker to search for a substring at the start of |
99 | // the blob than in the middle or at the end. |
100 | $dbOptions = [ 'version' => self::PREFERENCE_VERSION ] + $options; |
101 | |
102 | $this->saveOptionsInternal( $userIdentity, FormatJson::encode( $dbOptions ) ); |
103 | } |
104 | |
105 | /** |
106 | * Log the user saving their settings |
107 | * |
108 | * Differentiate between users changing existing settings and users saving settings |
109 | * for the first time |
110 | * |
111 | * @param UserIdentity $userIdentity |
112 | */ |
113 | private function logSettingsChange( UserIdentity $userIdentity ) { |
114 | $currentOptions = $this->userOptionsManager->getOption( |
115 | $userIdentity, |
116 | self::PREFERENCE_NAME, |
117 | false |
118 | ); |
119 | |
120 | // $currentOptions is `false` if the user is saving their settings for the |
121 | // first time, or a string with the user's option otherwise |
122 | if ( $currentOptions === false ) { |
123 | $this->statsdDataFactory->increment( 'globalwatchlist.settings.new' ); |
124 | } else { |
125 | $this->statsdDataFactory->increment( 'globalwatchlist.settings.change' ); |
126 | } |
127 | } |
128 | |
129 | /** |
130 | * Actually save user options in the database |
131 | * |
132 | * @param UserIdentity $userIdentity |
133 | * @param string $options |
134 | */ |
135 | private function saveOptionsInternal( UserIdentity $userIdentity, string $options ) { |
136 | $this->logger->info( |
137 | "Saving options for {username}: {userOptions}", |
138 | [ |
139 | 'username' => $userIdentity->getName(), |
140 | 'userOptions' => $options |
141 | ] |
142 | ); |
143 | |
144 | $this->userOptionsManager->setOption( |
145 | $userIdentity, |
146 | self::PREFERENCE_NAME, |
147 | $options |
148 | ); |
149 | |
150 | $this->userOptionsManager->saveOptions( $userIdentity ); |
151 | } |
152 | |
153 | } |