Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialListObjectsByType
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 5
306
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDescription
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
56
 fetchZObjects
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2/**
3 * WikiLambda Special:ListObjectsByType page
4 *
5 * @file
6 * @ingroup Extensions
7 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
8 * @license MIT
9 */
10
11namespace MediaWiki\Extension\WikiLambda\Special;
12
13use MediaWiki\Extension\WikiLambda\Registry\ZLangRegistry;
14use MediaWiki\Extension\WikiLambda\Registry\ZTypeRegistry;
15use MediaWiki\Extension\WikiLambda\ZObjectStore;
16use MediaWiki\Languages\LanguageFallback;
17use MediaWiki\SpecialPage\SpecialPage;
18
19class SpecialListObjectsByType extends SpecialPage {
20
21    private ZObjectStore $zObjectStore;
22    private LanguageFallback $languageFallback;
23
24    /**
25     * @param ZObjectStore $zObjectStore
26     * @param LanguageFallback $languageFallback
27     */
28    public function __construct( ZObjectStore $zObjectStore, LanguageFallback $languageFallback ) {
29        parent::__construct( 'ListObjectsByType' );
30        $this->zObjectStore = $zObjectStore;
31        $this->languageFallback = $languageFallback;
32    }
33
34    /**
35     * @inheritDoc
36     */
37    protected function getGroupName() {
38        // Triggers use of message specialpages-group-wikilambda
39        return 'wikilambda';
40    }
41
42    /**
43     * @inheritDoc
44     */
45    public function getDescription() {
46        return $this->msg( 'wikilambda-special-objectsbytype' );
47    }
48
49    /**
50     * @inheritDoc
51     */
52    public function execute( $type ) {
53        $this->setHeaders();
54
55        $output = $this->getOutput();
56
57        $output->enableOOUI();
58
59        $output->addModuleStyles( [ 'mediawiki.special' ] );
60        // TODO (T300519): Make this help page.
61        $this->addHelpLink( 'Help:Wikifunctions/Objects by type' );
62
63        $langRegistry = ZLangRegistry::singleton();
64
65        // Make list of fallback language Zids
66        $languages = array_merge(
67            [ $this->getLanguage()->getCode() ],
68            $this->languageFallback->getAll(
69                $this->getLanguage()->getCode(),
70                /* Try for en, even if it's not an explicit fallback. */ LanguageFallback::MESSAGES
71            )
72        );
73
74        $languageZids = $langRegistry->getLanguageZids( $languages );
75
76        $typesList = $this->fetchZObjects( ZTypeRegistry::Z_TYPE, $languageZids );
77
78        $wikitext = '';
79        if ( $type !== null && $type !== '' && isset( $typesList[$type] ) ) {
80            $typeLabel = $typesList[$type];
81            $zobjectList = $this->fetchZObjects( $type, $languageZids );
82            $wikitext .= "\n== ";
83            $wikitext .= $this->msg( 'wikilambda-special-objectsbytype-listheader' )
84                ->rawParams( htmlspecialchars( $typeLabel ), $type )
85                ->parse();
86            $wikitext .= " ==\n";
87            foreach ( $zobjectList as $zid => $label ) {
88                // Let the usual linker de-reference the label as appropriate
89                $wikitext .= "# [[$zid]]\n";
90            }
91
92            if ( count( $zobjectList ) === 0 ) {
93                $wikitext .= $this->msg( 'wikilambda-special-objectsbytype-empty' );
94            }
95        }
96        $wikitext .= "\n== ";
97        $wikitext .= $this->msg( 'wikilambda-special-objectsbytype-typeheader' );
98        $wikitext .= " ==\n";
99        $wikitext .= $this->msg( 'wikilambda-special-objectsbytype-summary' );
100        $wikitext .= "\n";
101        foreach ( $typesList as $type => $label ) {
102            $wikitext .= ": [[Special:ListObjectsByType/$type|$label]] ($type)\n";
103        }
104
105        $output->addWikiTextAsInterface( $wikitext );
106    }
107
108    /**
109     * Use ZObjectStore to fetch all ZObjects with appropriate labels for the provided type
110     *
111     * @param string $type
112     * @param string[] $languageZids
113     * @return array
114     */
115    private function fetchZObjects( $type, $languageZids ) {
116        // Don't take down the site; limit listings to 5000(!) rows regardless.
117        $pageLimit = 5000;
118
119        // Paginate our DB query at 100 items per request
120        $queryLimit = 100;
121
122        $continue = null;
123
124        $zobjects = [];
125
126        while ( $pageLimit > 0 ) {
127            $pageLimit -= $queryLimit;
128
129            $res = $this->zObjectStore->searchZObjectLabels(
130                '',
131                true,
132                $languageZids,
133                $type,
134                null,
135                false,
136                $continue,
137                $queryLimit
138            );
139
140            foreach ( $res as $row ) {
141                // Only set the label if we don't have one already, or if
142                // it's the primary label of the first-requested language.
143                // TODO (T362238): This means that if you're asking for uk > ru > en and we only have ru and en
144                // labels, we'll return whichever is first, rather than your preferred ru label over en.
145                if (
146                    !isset( $zobjects[$row->wlzl_zobject_zid] )
147                    || ( $row->wlzl_label_primary && array_search( $row->wlzl_language, $languageZids ) === 0 )
148                ) {
149                    $zobjects[$row->wlzl_zobject_zid] = $row->wlzl_label;
150                }
151                $continue = $row->wlzl_id;
152            }
153
154            if ( $res->numRows() < $queryLimit ) {
155                // We got fewer than our limit last time, so exit the loop.
156                break;
157            }
158        }
159
160        asort( $zobjects );
161
162        return $zobjects;
163    }
164}