Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 155
0.00% covered (danger)
0.00%
0 / 24
CRAP
0.00% covered (danger)
0.00%
0 / 1
WikiSet
0.00% covered (danger)
0.00%
0 / 155
0.00% covered (danger)
0.00%
0 / 24
1640
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 exists
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWikisRaw
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setWikisRaw
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setType
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 newFromRow
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 newFromName
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
12
 newFromID
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
12
 getDataForCache
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 loadFromCachedData
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 saveToDB
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 delete
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 purge
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getPerIdCacheKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPerNameCacheKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWikis
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 inSet
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getRestrictedGroups
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 getAllWikiSets
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
 getWikiSetForGroup
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
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
21namespace MediaWiki\Extension\CentralAuth;
22
23use MediaWiki\MediaWikiServices;
24use MediaWiki\WikiMap\WikiMap;
25use stdClass;
26use Wikimedia\ObjectCache\WANObjectCache;
27use Wikimedia\Rdbms\Database;
28
29class 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}