Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 141
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialSearchWiki
0.00% covered (danger)
0.00%
0 / 141
0.00% covered (danger)
0.00%
0 / 10
702
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
 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 / 16
0.00% covered (danger)
0.00%
0 / 1
20
 showForm
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
6
 getLanguageOptions
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 doSearch
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
110
 strip
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 goToWiki
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 getRootCode
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 showMultipleResults
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace MediaWiki\Extension\WikimediaIncubator;
4
5use HTMLForm;
6use MediaWiki\Html\Html;
7use MediaWiki\Languages\LanguageFactory;
8use MediaWiki\Languages\LanguageNameUtils;
9use MediaWiki\SpecialPage\IncludableSpecialPage;
10use MediaWiki\Title\Title;
11use Xml;
12
13class SpecialSearchWiki extends IncludableSpecialPage {
14
15    /** @var array[] */
16    private $mProjects;
17
18    /** @var string[] */
19    private $mNamesUserLang;
20
21    /** @var string[] */
22    private $mEnglishNames;
23
24    /** @var string[] */
25    private $mNativeNames;
26
27    /** @var LanguageNameUtils */
28    private $languageNameUtils;
29
30    /** @var LanguageFactory */
31    private $languageFactory;
32
33    /**
34     * @param LanguageNameUtils $languageNameUtils
35     * @param LanguageFactory $languageFactory
36     */
37    public function __construct(
38        LanguageNameUtils $languageNameUtils,
39        LanguageFactory $languageFactory
40    ) {
41        parent::__construct( 'SearchWiki' );
42        $this->languageNameUtils = $languageNameUtils;
43        $this->languageFactory = $languageFactory;
44    }
45
46    public function getDescription() {
47        return $this->msg( 'wminc-searchwiki' );
48    }
49
50    /**
51     * Show the special page
52     * @param string|null $subpage parameter passed to the page or null
53     */
54    public function execute( $subpage ) {
55        global $wmincProjects;
56
57        $this->setHeaders();
58
59        # Array of projects
60        $this->mProjects = $wmincProjects;
61
62        # Queries
63        if ( $subpage ) {
64            $subpage = explode( '/', $subpage );
65        }
66        $projectQuery = $this->getRequest()->getText( 'searchproject', $subpage[0] ?? '' );
67        $languageQuery = $this->getRequest()->getText( 'searchlanguage', $subpage[1] ?? '' );
68
69        $this->showForm( $projectQuery, $languageQuery );
70
71        # Search
72        if ( $projectQuery || $languageQuery ) {
73            $this->mNamesUserLang = $this->languageNameUtils
74                ->getLanguageNames( $this->getLanguage()->getCode(), LanguageNameUtils::ALL );
75            $this->mEnglishNames = $this->languageNameUtils
76                ->getLanguageNames( 'en', LanguageNameUtils::ALL );
77            $this->mNativeNames = $this->languageNameUtils
78                ->getLanguageNames( LanguageNameUtils::AUTONYMS, LanguageNameUtils::ALL );
79            $this->doSearch( $projectQuery, $languageQuery );
80        }
81    }
82
83    /**
84     * Output the search form.
85     * @param string $project Default value for project field
86     * @param string $language Default value for language field
87     */
88    protected function showForm( $project, $language ) {
89        $projectList = [];
90        foreach ( $this->mProjects as $projectCode => $projectMetadata ) {
91            $projectList[$projectMetadata['name']] = $projectCode;
92        }
93
94        $form = HTMLForm::factory(
95            'ooui',
96            [
97                'Project' => [
98                    'type' => 'select',
99                    'name' => 'searchproject',
100                    'id' => 'wminc-searchproject',
101                    'options' => $projectList,
102                    'label-message' => 'wminc-searchwiki-selectproject',
103                    'default' => $project,
104                ],
105                'Language' => [
106                    'type' => 'combobox',
107                    'options' => $this->getLanguageOptions(),
108                    'name' => 'searchlanguage',
109                    'id' => 'wminc-searchlanguage',
110                    'size' => 30,
111                    'label-message' => 'wminc-searchwiki-inputlanguage',
112                    'default' => $language,
113                ],
114            ],
115            $this->getContext()
116        );
117        $form->setMethod( 'get' )
118            ->setWrapperLegendMsg( 'wminc-searchwiki' )
119            ->setSubmitTextMsg( 'wminc-searchwiki-go' )
120            ->setId( 'wminc-searchwiki-form' )
121            ->prepareForm()
122            ->displayForm( false );
123    }
124
125    /**
126     * Get a list of supported languages for use by the language form field.
127     * Code stolen from SpecialPageLanguage
128     *
129     * @return array
130     */
131    private function getLanguageOptions() {
132        $userLang = $this->getLanguage()->getCode();
133        $languages = $this->languageNameUtils->getLanguageNames( $userLang, LanguageNameUtils::SUPPORTED );
134        ksort( $languages );
135        $options = [];
136        foreach ( $languages as $code => $name ) {
137            $options["$code - $name"] = $name;
138        }
139        return $options;
140    }
141
142    /**
143     * @param string $projectQuery
144     * @param string $languageQuery
145     */
146    protected function doSearch( $projectQuery, $languageQuery ) {
147        ## Match project ##
148        if ( isset( $this->mProjects[$projectQuery] ) ) {
149            # searched with a project code (select box)
150            $matchProject = $projectQuery;
151        } else {
152            $projectCode = array_search( $projectQuery, array_column( $this->mProjects, 'name' ) );
153            if ( $projectCode ) {
154                # searched with a project name
155                $matchProject = $projectCode;
156            } else {
157                $this->getOutput()->addWikiMsg( 'wminc-searchwiki-noproject' );
158                return;
159            }
160        }
161
162        $results = [];
163
164        $lcLanguageQuery = strtolower( $languageQuery );
165        # The more important, the more below, because they override earlier codes
166        $validCodes = array_keys( $this->languageNameUtils->getLanguageNames( 'en', LanguageNameUtils::ALL ) );
167        if ( in_array( $lcLanguageQuery, $validCodes ) ) {
168            $builtinCode = $this->languageFactory->getLanguage( $lcLanguageQuery )->getCode();
169            $results[$builtinCode] = 'langcode'; # Match language code
170        }
171        $lcLanguageQuery = self::strip( $languageQuery );
172
173        $codeByEnglishName = array_search(
174            $lcLanguageQuery, array_map( [ self::class, 'strip' ], $this->mEnglishNames )
175        );
176        if ( $codeByEnglishName ) {
177            $results[$codeByEnglishName] = 'englishname'; # Match name in English
178        }
179
180        $codeUserLang = array_search(
181            $lcLanguageQuery, array_map( [ self::class, 'strip' ], $this->mNamesUserLang )
182        );
183        if ( $codeUserLang ) {
184            $results[$codeUserLang] = 'userlangname'; # Match name in user language
185        }
186
187        $codeByNativeName = array_search(
188            $lcLanguageQuery, array_map( [ self::class, 'strip' ], $this->mNativeNames )
189        );
190        if ( $codeByNativeName ) {
191            $results[$codeByNativeName] = 'nativename'; # Match native name
192        }
193
194        if ( count( $results ) === 1 ) {
195            $this->gotoWiki( $matchProject, key( $results ) );
196        } elseif ( count( $results ) < 1 ) {
197            $noresult = Html::element( 'p', [ 'class' => 'error' ],
198                $this->msg( 'wminc-searchwiki-noresults' )->text() );
199            $this->getOutput()->addHTML( $noresult );
200            return;
201        } elseif ( count( $results ) > 1 ) {
202            $this->showMultipleResults( $matchProject, $languageQuery, $results );
203        }
204    }
205
206    /**
207     * Improve search by increasing the chance of matches
208     * @param string $str
209     * @return string
210     */
211    protected static function strip( $str ) {
212        $str = strtolower( trim( $str ) );
213        $replace = [ ' ' => '', '-' => '' ];
214        return str_replace( array_keys( $replace ), array_values( $replace ), $str );
215    }
216
217    /**
218     * Go directly to the wiki if it exists, else to the Incubator test wiki
219     * @param string $project project code
220     * @param string $lang Language code
221     */
222    protected function goToWiki( $project, $lang ) {
223        $lang = $this->getRootCode( $lang );
224        $dbarray = [ 'project' => $project, 'lang' => $lang, 'error' => null ];
225        $status = WikimediaIncubator::getDBState( $dbarray );
226        $infopageParams = [
227            'goto' => 'mainpage',
228            'uselang' => $this->getRequest()->getVal( 'uselang' )
229        ];
230        $url = $status == 'existing' ? WikimediaIncubator::getSubdomain(
231                $this->getUser(),
232                $lang,
233                $project
234            ) :
235            Title::newFromText( 'W' . $project . '/' . $lang )->getFullURL( $infopageParams );
236        $this->getOutput()->redirect( $url );
237    }
238
239    /**
240     * @param string $code
241     * @return string
242     */
243    protected function getRootCode( $code ) {
244        # e.g. ks-arab -> ks
245        $stripLangTags = [ '-arab', '-latn', '-cyrl', '-deva', '-cans', '-grek' ];
246        return str_replace( $stripLangTags, '', $code );
247    }
248
249    /**
250     * @param string $project
251     * @param string $languageQuery
252     * @param array $results
253     */
254    protected function showMultipleResults( $project, $languageQuery, $results ) {
255        $this->getOutput()->addHTML( '<div id="wminc-searchwiki-results">' .
256            Xml::element( 'p', [],
257                $this->msg( 'wminc-searchwiki-multiplematches' )->text() ) .
258            '<ul>'
259        );
260        $linkRenderer = $this->getLinkRenderer();
261        foreach ( $results as $resultCode => $resultType ) {
262            $langName = $this->mNamesUserLang[$resultCode];
263            $infopage = Title::newFromText(
264                WikimediaIncubator::displayPrefix( $project, $resultCode, true )
265            );
266            $linkInfoPage = $linkRenderer->makeKnownLink( $infopage,
267                $this->msg( 'wminc-searchwiki-gotoinfopage' )->text()
268            );
269            # Give grep a chance to find the usages:
270            # wminc-infopage-title-p, wminc-infopage-title-b, wminc-infopage-title-t,
271            # wminc-infopage-title-q, wminc-infopage-title-n, wminc-infopage-title-s,
272            # wminc-infopage-title-v, wminc-infopage-title-y
273            $linkMainPage = $linkRenderer->makeKnownLink( $infopage,
274                $this->msg( 'wminc-infopage-title-' . $project, $langName )->text(),
275                [], [ 'goto' => 'mainpage' ]
276            );
277            # Give grep a chance to find the usages:
278            # wminc-searchwiki-match-langcode, wminc-searchwiki-match-englishname,
279            # wminc-searchwiki-match-userlangname, wminc-searchwiki-match-nativename
280            $this->getOutput()->addHTML( '<li>' .
281                $this->msg( 'wminc-searchwiki-match-' . $resultType,
282                    $languageQuery, $langName, $resultCode )->escaped() . ' ' .
283                $this->getLanguage()->getArrow() . ' ' .
284                $this->msg( 'wminc-searchwiki-goto', $linkMainPage, $linkInfoPage )->parse() .
285            '</li>' );
286        }
287        $this->getOutput()->addHTML( '</ul></div>' );
288    }
289}