Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
47.65% covered (danger)
47.65%
71 / 149
50.00% covered (danger)
50.00%
3 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
SiteLibrary
47.65% covered (danger)
47.65%
71 / 149
50.00% covered (danger)
50.00%
3 / 6
159.11
0.00% covered (danger)
0.00%
0 / 1
 register
41.25% covered (danger)
41.25%
33 / 80
0.00% covered (danger)
0.00%
0 / 1
35.54
 pagesInCategory
78.26% covered (warning)
78.26%
18 / 23
0.00% covered (danger)
0.00%
0 / 1
5.26
 pagesInNamespace
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 usersInGroup
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getNsIndex
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 interwikiMap
33.33% covered (danger)
33.33%
13 / 39
0.00% covered (danger)
0.00%
0 / 1
46.85
1<?php
2
3namespace MediaWiki\Extension\Scribunto\Engines\LuaCommon;
4
5use MediaWiki\Category\Category;
6use MediaWiki\MainConfigNames;
7use MediaWiki\MediaWikiServices;
8use MediaWiki\SiteStats\SiteStats;
9use MediaWiki\Specials\SpecialVersion;
10use MediaWiki\Title\Title;
11
12class SiteLibrary extends LibraryBase {
13    /** @var string|null */
14    private static $namespacesCacheLang = null;
15    /** @var array[]|null */
16    private static $namespacesCache = null;
17    /** @var array[] */
18    private static $interwikiMapCache = [];
19    /** @var int[][] */
20    private $pagesInCategoryCache = [];
21
22    public function register() {
23        $lib = [
24            'getNsIndex' => [ $this, 'getNsIndex' ],
25            'pagesInCategory' => [ $this, 'pagesInCategory' ],
26            'pagesInNamespace' => [ $this, 'pagesInNamespace' ],
27            'usersInGroup' => [ $this, 'usersInGroup' ],
28            'interwikiMap' => [ $this, 'interwikiMap' ],
29        ];
30        $parser = $this->getParser();
31        $services = MediaWikiServices::getInstance();
32        $config = $services->getMainConfig();
33        $contLang = $services->getContentLanguage();
34        $info = [
35            'siteName' => $config->get( MainConfigNames::Sitename ),
36            'server' => $config->get( MainConfigNames::Server ),
37            'scriptPath' => $config->get( MainConfigNames::ScriptPath ),
38            'stylePath' => $config->get( MainConfigNames::StylePath ),
39            'currentVersion' => SpecialVersion::getVersion(
40                '', $parser ? $parser->getTargetLanguage() : $contLang
41            ),
42        ];
43
44        if ( !self::$namespacesCache || self::$namespacesCacheLang !== $contLang->getCode() ) {
45            $namespaces = [];
46            $namespacesByName = [];
47            $namespaceInfo = $services->getNamespaceInfo();
48            foreach ( $contLang->getFormattedNamespaces() as $ns => $title ) {
49                $canonical = $namespaceInfo->getCanonicalName( $ns );
50                $namespaces[$ns] = [
51                    'id' => $ns,
52                    'name' => $title,
53                    'canonicalName' => strtr( $canonical, '_', ' ' ),
54                    'hasSubpages' => $namespaceInfo->hasSubpages( $ns ),
55                    'hasGenderDistinction' => $namespaceInfo->hasGenderDistinction( $ns ),
56                    'isCapitalized' => $namespaceInfo->isCapitalized( $ns ),
57                    'isContent' => $namespaceInfo->isContent( $ns ),
58                    'isIncludable' => !$namespaceInfo->isNonincludable( $ns ),
59                    'isMovable' => $namespaceInfo->isMovable( $ns ),
60                    'isSubject' => $namespaceInfo->isSubject( $ns ),
61                    'isTalk' => $namespaceInfo->isTalk( $ns ),
62                    'defaultContentModel' => $namespaceInfo->getNamespaceContentModel( $ns ),
63                    'aliases' => [],
64                ];
65                if ( $ns >= NS_MAIN ) {
66                    $namespaces[$ns]['subject'] = $namespaceInfo->getSubject( $ns );
67                    $namespaces[$ns]['talk'] = $namespaceInfo->getTalk( $ns );
68                    $namespaces[$ns]['associated'] = $namespaceInfo->getAssociated( $ns );
69                } else {
70                    $namespaces[$ns]['subject'] = $ns;
71                }
72                $namespacesByName[strtr( $title, ' ', '_' )] = $ns;
73                if ( $canonical ) {
74                    $namespacesByName[$canonical] = $ns;
75                }
76            }
77
78            $namespaceAliases = $config->get( MainConfigNames::NamespaceAliases );
79            $aliases = array_merge( $namespaceAliases, $contLang->getNamespaceAliases() );
80            foreach ( $aliases as $title => $ns ) {
81                if ( !isset( $namespacesByName[$title] ) && isset( $namespaces[$ns] ) ) {
82                    $ct = count( $namespaces[$ns]['aliases'] );
83                    $namespaces[$ns]['aliases'][$ct + 1] = $title;
84                    $namespacesByName[$title] = $ns;
85                }
86            }
87
88            $namespaces[NS_MAIN]['displayName'] = wfMessage( 'blanknamespace' )->inContentLanguage()->text();
89
90            self::$namespacesCache = $namespaces;
91            self::$namespacesCacheLang = $contLang->getCode();
92        }
93        $info['namespaces'] = self::$namespacesCache;
94
95        if ( defined( 'MW_PHPUNIT_TEST' ) ) {
96            $info['stats'] = [
97                'pages' => 1,
98                'articles' => 1,
99                'files' => 0,
100                'edits' => 1,
101                'users' => 1,
102                'activeUsers' => 1,
103                'admins' => 1,
104            ];
105        } else {
106            $info['stats'] = [
107                'pages' => (int)SiteStats::pages(),
108                'articles' => (int)SiteStats::articles(),
109                'files' => (int)SiteStats::images(),
110                'edits' => (int)SiteStats::edits(),
111                'users' => (int)SiteStats::users(),
112                'activeUsers' => (int)SiteStats::activeUsers(),
113                'admins' => (int)SiteStats::numberingroup( 'sysop' ),
114            ];
115        }
116
117        return $this->getEngine()->registerInterface( 'mw.site.lua', $lib, $info );
118    }
119
120    /**
121     * Handler for pagesInCategory
122     * @internal
123     * @param string|null $category
124     * @param string|null $which
125     * @return int[]|int[][]
126     */
127    public function pagesInCategory( $category = null, $which = null ) {
128        $this->checkType( 'pagesInCategory', 1, $category, 'string' );
129        $this->checkTypeOptional( 'pagesInCategory', 2, $which, 'string', 'all' );
130
131        $title = Title::makeTitleSafe( NS_CATEGORY, $category );
132        if ( !$title ) {
133            return [ 0 ];
134        }
135        $cacheKey = $title->getDBkey();
136
137        if ( !isset( $this->pagesInCategoryCache[$cacheKey] ) ) {
138            $this->incrementExpensiveFunctionCount();
139            $category = Category::newFromTitle( $title );
140            $counts = [
141                'all' => $category->getMemberCount(),
142                'subcats' => $category->getSubcatCount(),
143                'files' => $category->getFileCount(),
144                'pages' => $category->getPageCount( Category::COUNT_CONTENT_PAGES ),
145            ];
146            $this->pagesInCategoryCache[$cacheKey] = $counts;
147        }
148        if ( $which === '*' ) {
149            return [ $this->pagesInCategoryCache[$cacheKey] ];
150        }
151        if ( !isset( $this->pagesInCategoryCache[$cacheKey][$which] ) ) {
152            $this->checkType(
153                'pagesInCategory', 2, $which, "one of '*', 'all', 'pages', 'subcats', or 'files'"
154            );
155        }
156        return [ $this->pagesInCategoryCache[$cacheKey][$which] ];
157    }
158
159    /**
160     * Handler for pagesInNamespace
161     * @internal
162     * @param int|string|null $ns
163     * @return int[]
164     */
165    public function pagesInNamespace( $ns = null ) {
166        $this->checkType( 'pagesInNamespace', 1, $ns, 'number' );
167        return [ (int)SiteStats::pagesInNs( intval( $ns ) ) ];
168    }
169
170    /**
171     * Handler for usersInGroup
172     * @internal
173     * @param string|null $group
174     * @return int[]
175     */
176    public function usersInGroup( $group = null ) {
177        $this->checkType( 'usersInGroup', 1, $group, 'string' );
178        return [ (int)SiteStats::numberingroup( strtolower( $group ) ) ];
179    }
180
181    /**
182     * Handler for getNsIndex
183     * @internal
184     * @param string|null $name
185     * @return int[]|bool[]
186     */
187    public function getNsIndex( $name = null ) {
188        $this->checkType( 'getNsIndex', 1, $name, 'string' );
189        // PHP call is case-insensitive but chokes on non-standard spaces/underscores.
190        $name = trim( preg_replace( '/[\s_]+/', '_', $name ), '_' );
191        return [ MediaWikiServices::getInstance()->getContentLanguage()->getNsIndex( $name ) ];
192    }
193
194    /**
195     * Handler for interwikiMap
196     * @internal
197     * @param string|null $filter
198     * @return array[]
199     */
200    public function interwikiMap( $filter = null ) {
201        $this->checkTypeOptional( 'interwikiMap', 1, $filter, 'string', null );
202        $local = null;
203        if ( $filter === 'local' ) {
204            $local = true;
205        } elseif ( $filter === '!local' ) {
206            $local = false;
207        } elseif ( $filter !== null ) {
208            throw new LuaError(
209                "bad argument #1 to 'interwikiMap' (unknown filter '$filter')"
210            );
211        }
212        $cacheKey = $filter ?? 'null';
213        if ( !isset( self::$interwikiMapCache[$cacheKey] ) ) {
214            // Not expensive because we can have a max of three cache misses in the
215            // entire page parse.
216            $interwikiMap = [];
217            $lookup = MediaWikiServices::getInstance()->getInterwikiLookup();
218            $prefixes = $lookup->getAllPrefixes( $local );
219            $config = MediaWikiServices::getInstance()->getMainConfig();
220            $localInterwikis = $config->get( MainConfigNames::LocalInterwikis );
221            $extraInterlanguageLinkPrefixes = $config->get( MainConfigNames::ExtraInterlanguageLinkPrefixes );
222            foreach ( $prefixes as $row ) {
223                $prefix = $row['iw_prefix'];
224                $val = [
225                    'prefix' => $prefix,
226                    'url' => wfExpandUrl( $row['iw_url'], PROTO_RELATIVE ),
227                    'isProtocolRelative' => substr( $row['iw_url'], 0, 2 ) === '//',
228                    'isLocal' => isset( $row['iw_local'] ) && $row['iw_local'] == '1',
229                    'isTranscludable' => isset( $row['iw_trans'] ) && $row['iw_trans'] == '1',
230                    'isCurrentWiki' => in_array( $prefix, $localInterwikis ),
231                    'isExtraLanguageLink' => in_array( $prefix, $extraInterlanguageLinkPrefixes ),
232                ];
233                if ( $val['isExtraLanguageLink'] ) {
234                    $displayText = wfMessage( "interlanguage-link-$prefix" );
235                    if ( !$displayText->isDisabled() ) {
236                        $val['displayText'] = $displayText->text();
237                    }
238                    $tooltip = wfMessage( "interlanguage-link-sitename-$prefix" );
239                    if ( !$tooltip->isDisabled() ) {
240                        $val['tooltip'] = $tooltip->text();
241                    }
242                }
243                $interwikiMap[$prefix] = $val;
244            }
245            self::$interwikiMapCache[$cacheKey] = $interwikiMap;
246        }
247        return [ self::$interwikiMapCache[$cacheKey] ];
248    }
249}