Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 155 |
|
0.00% |
0 / 24 |
CRAP | |
0.00% |
0 / 1 |
WikiSet | |
0.00% |
0 / 155 |
|
0.00% |
0 / 24 |
1640 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getId | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
exists | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getWikisRaw | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setWikisRaw | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setType | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
newFromRow | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
newFromName | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
12 | |||
newFromID | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
12 | |||
getDataForCache | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
loadFromCachedData | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
saveToDB | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
6 | |||
delete | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
purge | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getPerIdCacheKey | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPerNameCacheKey | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getWikis | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
inSet | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getRestrictedGroups | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
getAllWikiSets | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
30 | |||
getWikiSetForGroup | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | namespace MediaWiki\Extension\CentralAuth; |
22 | |
23 | use MediaWiki\MediaWikiServices; |
24 | use MediaWiki\WikiMap\WikiMap; |
25 | use stdClass; |
26 | use Wikimedia\ObjectCache\WANObjectCache; |
27 | use Wikimedia\Rdbms\Database; |
28 | |
29 | class WikiSet { |
30 | |
31 | public const OPTIN = 'optin'; |
32 | public const OPTOUT = 'optout'; |
33 | |
34 | private const VERSION = 1; |
35 | |
36 | /** @var int ID of the group */ |
37 | private $mId; |
38 | /** @var string Display name of the group */ |
39 | private $mName; |
40 | /** @var string Opt-in based or opt-out based */ |
41 | private $mType; |
42 | /** @var string[] List of wikis */ |
43 | private $mWikis; |
44 | |
45 | /** @var string[] */ |
46 | private static $mCacheVars = [ |
47 | 'mId', |
48 | 'mName', |
49 | 'mType', |
50 | 'mWikis', |
51 | ]; |
52 | |
53 | /** |
54 | * @param string $name |
55 | * @param string $type |
56 | * @param string[] $wikis |
57 | * @param int $id |
58 | */ |
59 | public function __construct( $name = '', $type = self::OPTIN, $wikis = [], $id = 0 ) { |
60 | $this->mId = $id; |
61 | $this->mName = $name; |
62 | $this->mType = $type; |
63 | $this->mWikis = $wikis; |
64 | } |
65 | |
66 | /** |
67 | * @return int |
68 | */ |
69 | public function getId() { |
70 | return $this->mId; |
71 | } |
72 | |
73 | /** |
74 | * @return bool |
75 | */ |
76 | public function exists() { |
77 | return (bool)$this->getID(); |
78 | } |
79 | |
80 | /** |
81 | * @return string |
82 | */ |
83 | public function getName() { |
84 | return $this->mName; |
85 | } |
86 | |
87 | /** |
88 | * @param string $name |
89 | */ |
90 | public function setName( $name ) { |
91 | $this->mName = $name; |
92 | } |
93 | |
94 | /** |
95 | * @return string[] |
96 | */ |
97 | public function getWikisRaw() { |
98 | return $this->mWikis; |
99 | } |
100 | |
101 | /** |
102 | * @param string[] $wikis |
103 | */ |
104 | public function setWikisRaw( $wikis ) { |
105 | $this->mWikis = $wikis; |
106 | } |
107 | |
108 | /** |
109 | * @return string |
110 | */ |
111 | public function getType() { |
112 | return $this->mType; |
113 | } |
114 | |
115 | /** |
116 | * @param string $type |
117 | */ |
118 | public function setType( $type ) { |
119 | if ( !in_array( $type, [ self::OPTIN, self::OPTOUT ] ) ) { |
120 | return; |
121 | } |
122 | $this->mType = $type; |
123 | } |
124 | |
125 | /** |
126 | * @param stdClass|bool $row |
127 | * @return null|WikiSet |
128 | */ |
129 | public static function newFromRow( $row ) { |
130 | if ( !$row ) { |
131 | return null; |
132 | } |
133 | return new WikiSet( |
134 | $row->ws_name, |
135 | $row->ws_type, |
136 | explode( ',', $row->ws_wikis ), |
137 | $row->ws_id |
138 | ); |
139 | } |
140 | |
141 | /** |
142 | * @param string $name |
143 | * @return null|WikiSet |
144 | */ |
145 | public static function newFromName( $name ) { |
146 | $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); |
147 | $fname = __METHOD__; |
148 | |
149 | $data = $cache->getWithSetCallback( |
150 | self::getPerNameCacheKey( $cache, $name ), |
151 | $cache::TTL_INDEFINITE, |
152 | function ( $oldValue, &$ttl, &$setOpts ) use ( $name, $fname ) { |
153 | $dbr = CentralAuthServices::getDatabaseManager()->getCentralReplicaDB(); |
154 | $setOpts += Database::getCacheSetOptions( $dbr ); |
155 | |
156 | $row = $dbr->newSelectQueryBuilder() |
157 | ->select( '*' ) |
158 | ->from( 'wikiset' ) |
159 | ->where( [ 'ws_name' => $name ] ) |
160 | ->caller( $fname ) |
161 | ->fetchRow(); |
162 | |
163 | $wikiSet = self::newFromRow( $row ); |
164 | if ( $wikiSet ) { |
165 | $value = $wikiSet->getDataForCache(); |
166 | } else { |
167 | // cache negatives |
168 | $ttl = WANObjectCache::TTL_MINUTE; |
169 | $value = null; |
170 | } |
171 | |
172 | return $value; |
173 | }, |
174 | [ 'version' => self::VERSION ] |
175 | ); |
176 | |
177 | if ( !$data ) { |
178 | return null; |
179 | } |
180 | |
181 | $wikiSet = new WikiSet(); |
182 | $wikiSet->loadFromCachedData( $data ); |
183 | |
184 | return $wikiSet; |
185 | } |
186 | |
187 | /** |
188 | * @param string|int $id |
189 | * @return null|WikiSet |
190 | */ |
191 | public static function newFromID( $id ) { |
192 | $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); |
193 | $fname = __METHOD__; |
194 | |
195 | $data = $cache->getWithSetCallback( |
196 | self::getPerIdCacheKey( $cache, $id ), |
197 | $cache::TTL_INDEFINITE, |
198 | function ( $oldValue, &$ttl, &$setOpts ) use ( $id, $fname ) { |
199 | $dbr = CentralAuthServices::getDatabaseManager()->getCentralReplicaDB(); |
200 | $setOpts += Database::getCacheSetOptions( $dbr ); |
201 | |
202 | $row = $dbr->newSelectQueryBuilder() |
203 | ->select( '*' ) |
204 | ->from( 'wikiset' ) |
205 | ->where( [ 'ws_id' => $id ] ) |
206 | ->caller( $fname ) |
207 | ->fetchRow(); |
208 | |
209 | $wikiSet = self::newFromRow( $row ); |
210 | if ( $wikiSet ) { |
211 | $value = $wikiSet->getDataForCache(); |
212 | } else { |
213 | // cache negatives |
214 | $ttl = WANObjectCache::TTL_MINUTE; |
215 | $value = null; |
216 | } |
217 | |
218 | return $value; |
219 | }, |
220 | [ 'version' => self::VERSION ] |
221 | ); |
222 | |
223 | if ( !$data ) { |
224 | return null; |
225 | } |
226 | |
227 | $wikiSet = new WikiSet(); |
228 | $wikiSet->loadFromCachedData( $data ); |
229 | |
230 | return $wikiSet; |
231 | } |
232 | |
233 | /** |
234 | * @return array |
235 | */ |
236 | private function getDataForCache() { |
237 | $data = []; |
238 | foreach ( self::$mCacheVars as $var ) { |
239 | if ( $this->$var !== null ) { |
240 | $data[$var] = $this->$var; |
241 | } |
242 | } |
243 | |
244 | return $data; |
245 | } |
246 | |
247 | /** |
248 | * @param array $data |
249 | */ |
250 | private function loadFromCachedData( array $data ) { |
251 | foreach ( $data as $key => $val ) { |
252 | $this->$key = $val; |
253 | } |
254 | } |
255 | |
256 | /** |
257 | * @return bool |
258 | */ |
259 | public function saveToDB() { |
260 | $dbw = CentralAuthServices::getDatabaseManager()->getCentralPrimaryDB(); |
261 | $dbw->startAtomic( __METHOD__ ); |
262 | $dbw->newReplaceQueryBuilder() |
263 | ->replaceInto( 'wikiset' ) |
264 | ->uniqueIndexFields( 'ws_id' ) |
265 | ->row( [ |
266 | 'ws_id' => $this->mId, |
267 | 'ws_name' => $this->mName, |
268 | 'ws_type' => $this->mType, |
269 | 'ws_wikis' => implode( ',', $this->mWikis ), |
270 | ] ) |
271 | ->caller( __METHOD__ ) |
272 | ->execute(); |
273 | if ( !$this->mId ) { |
274 | $this->mId = $dbw->insertId(); |
275 | } |
276 | $dbw->endAtomic( __METHOD__ ); |
277 | $this->purge(); |
278 | return (bool)$dbw->affectedRows(); |
279 | } |
280 | |
281 | /** |
282 | * @return bool |
283 | */ |
284 | public function delete() { |
285 | $dbw = CentralAuthServices::getDatabaseManager()->getCentralPrimaryDB(); |
286 | $dbw->newDeleteQueryBuilder() |
287 | ->deleteFrom( 'wikiset' ) |
288 | ->where( [ 'ws_id' => $this->mId ] ) |
289 | ->caller( __METHOD__ ) |
290 | ->execute(); |
291 | $this->purge(); |
292 | return (bool)$dbw->affectedRows(); |
293 | } |
294 | |
295 | public function purge() { |
296 | $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); |
297 | $cache->delete( self::getPerIdCacheKey( $cache, $this->mId ) ); |
298 | $cache->delete( self::getPerNameCacheKey( $cache, $this->mName ) ); |
299 | } |
300 | |
301 | /** |
302 | * @param WANObjectCache $cache |
303 | * @param string|int $id |
304 | * @return string |
305 | */ |
306 | private static function getPerIdCacheKey( WANObjectCache $cache, $id ) { |
307 | return $cache->makeGlobalKey( __CLASS__, 'id', $id ); |
308 | } |
309 | |
310 | /** |
311 | * @param WANObjectCache $cache |
312 | * @param string $name |
313 | * @return string |
314 | */ |
315 | private static function getPerNameCacheKey( WANObjectCache $cache, $name ) { |
316 | return $cache->makeGlobalKey( __CLASS__, 'name', md5( $name ) ); |
317 | } |
318 | |
319 | /** |
320 | * @return string[] |
321 | */ |
322 | public function getWikis() { |
323 | if ( $this->mType == self::OPTIN ) { |
324 | return $this->mWikis; |
325 | } else { |
326 | $wikiList = CentralAuthServices::getWikiListService()->getWikiList(); |
327 | return array_diff( $wikiList, $this->mWikis ); |
328 | } |
329 | } |
330 | |
331 | /** |
332 | * @param string $wiki |
333 | * @return bool |
334 | */ |
335 | public function inSet( $wiki = '' ) { |
336 | if ( !$wiki ) { |
337 | $wiki = WikiMap::getCurrentWikiId(); |
338 | } |
339 | return in_array( $wiki, $this->getWikis() ); |
340 | } |
341 | |
342 | /** |
343 | * @return string[] |
344 | */ |
345 | public function getRestrictedGroups() { |
346 | $dbr = CentralAuthServices::getDatabaseManager()->getCentralReplicaDB(); |
347 | return $dbr->newSelectQueryBuilder() |
348 | ->select( 'ggr_group' ) |
349 | ->from( 'global_group_restrictions' ) |
350 | ->where( [ 'ggr_set' => $this->mId ] ) |
351 | ->caller( __METHOD__ ) |
352 | ->fetchFieldValues(); |
353 | } |
354 | |
355 | /** |
356 | * @param string|null $from The wiki set name to start from (result is ordered by name) |
357 | * @param int|null $limit Limit for the selection (0 or null = no limit) |
358 | * @param bool $orderByName Order the result by name? |
359 | * @return self[] |
360 | */ |
361 | public static function getAllWikiSets( $from = null, $limit = null, $orderByName = false ) { |
362 | $dbr = CentralAuthServices::getDatabaseManager()->getCentralReplicaDB(); |
363 | |
364 | $qb = $dbr->newSelectQueryBuilder() |
365 | ->select( '*' ) |
366 | ->from( 'wikiset' ) |
367 | ->caller( __METHOD__ ); |
368 | |
369 | if ( $from != null ) { |
370 | $qb->where( $dbr->expr( 'ws_name', '>=', $from ) ); |
371 | $orderByName = true; |
372 | } |
373 | |
374 | if ( $limit ) { |
375 | $qb->limit( intval( $limit ) ); |
376 | } |
377 | |
378 | if ( $orderByName ) { |
379 | $qb->orderBy( 'ws_name' ); |
380 | } |
381 | |
382 | $res = $qb->fetchResultSet(); |
383 | |
384 | $result = []; |
385 | foreach ( $res as $row ) { |
386 | $result[] = self::newFromRow( $row ); |
387 | } |
388 | return $result; |
389 | } |
390 | |
391 | /** |
392 | * @param string $group |
393 | * @return int |
394 | */ |
395 | public static function getWikiSetForGroup( $group ) { |
396 | $dbr = CentralAuthServices::getDatabaseManager()->getCentralReplicaDB(); |
397 | return (int)$dbr->newSelectQueryBuilder() |
398 | ->select( 'ggr_set' ) |
399 | ->from( 'global_group_restrictions' ) |
400 | ->where( [ 'ggr_group' => $group ] ) |
401 | ->caller( __METHOD__ ) |
402 | ->fetchField(); |
403 | } |
404 | |
405 | } |