Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 147
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiQueryAllMessages
0.00% covered (danger)
0.00%
0 / 146
0.00% covered (danger)
0.00%
0 / 6
1892
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 95
0.00% covered (danger)
0.00%
0 / 1
1332
 getCacheMode
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
2
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getHelpUrls
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Api;
10
11use MediaWiki\Language\Language;
12use MediaWiki\Language\LocalisationCache;
13use MediaWiki\Language\MessageCache;
14use MediaWiki\Languages\LanguageFactory;
15use MediaWiki\Languages\LanguageNameUtils;
16use MediaWiki\Pager\AllMessagesTablePager;
17use MediaWiki\Title\Title;
18use Wikimedia\ParamValidator\ParamValidator;
19
20/**
21 * A query action to return messages from site message cache
22 *
23 * @ingroup API
24 */
25class ApiQueryAllMessages extends ApiQueryBase {
26
27    private Language $contentLanguage;
28    private LanguageFactory $languageFactory;
29    private LanguageNameUtils $languageNameUtils;
30    private LocalisationCache $localisationCache;
31    private MessageCache $messageCache;
32
33    public function __construct(
34        ApiQuery $query,
35        string $moduleName,
36        Language $contentLanguage,
37        LanguageFactory $languageFactory,
38        LanguageNameUtils $languageNameUtils,
39        LocalisationCache $localisationCache,
40        MessageCache $messageCache
41    ) {
42        parent::__construct( $query, $moduleName, 'am' );
43        $this->contentLanguage = $contentLanguage;
44        $this->languageFactory = $languageFactory;
45        $this->languageNameUtils = $languageNameUtils;
46        $this->localisationCache = $localisationCache;
47        $this->messageCache = $messageCache;
48    }
49
50    public function execute() {
51        $params = $this->extractRequestParams();
52        if ( $params['lang'] === null ) {
53            $langObj = $this->getLanguage();
54        } elseif ( !$this->languageNameUtils->isValidCode( $params['lang'] ) ) {
55            $this->dieWithError(
56                [ 'apierror-invalidlang', $this->encodeParamName( 'lang' ) ], 'invalidlang'
57            );
58        } else {
59            $langObj = $this->languageFactory->getLanguage( $params['lang'] );
60        }
61
62        if ( $params['enableparser'] ) {
63            if ( $params['title'] !== null ) {
64                $title = Title::newFromText( $params['title'] );
65                if ( !$title || $title->isExternal() ) {
66                    $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] );
67                }
68            } else {
69                $title = Title::newFromText( 'API' );
70            }
71        }
72
73        $prop = array_fill_keys( (array)$params['prop'], true );
74
75        // Determine which messages should we print
76        if ( in_array( '*', $params['messages'] ) ) {
77            $message_names = $this->localisationCache->getSubitemList( $langObj->getCode(), 'messages' ) ?? [];
78            if ( $params['includelocal'] ) {
79                $message_names = array_unique( array_merge(
80                    $message_names,
81                    // Pass in the content language code so we get local messages that have a
82                    // MediaWiki:msgkey page. We might theoretically miss messages that have no
83                    // MediaWiki:msgkey page but do have a MediaWiki:msgkey/lang page, but that's
84                    // just a stupid case.
85                    $this->messageCache->getAllMessageKeys( $this->contentLanguage->getCode() )
86                ) );
87            }
88            sort( $message_names );
89            $messages_target = $message_names;
90        } else {
91            $messages_target = $params['messages'];
92        }
93
94        // Filter messages that have the specified prefix
95        // Because we sorted the message array earlier, they will appear in a clump:
96        if ( isset( $params['prefix'] ) ) {
97            $skip = false;
98            $messages_filtered = [];
99            foreach ( $messages_target as $message ) {
100                if ( str_starts_with( $message, $params['prefix'] ) ) {
101                    if ( !$skip ) {
102                        $skip = true;
103                    }
104                    $messages_filtered[] = $message;
105                } elseif ( $skip ) {
106                    break;
107                }
108            }
109            $messages_target = $messages_filtered;
110        }
111
112        // Filter messages that contain specified string
113        if ( isset( $params['filter'] ) ) {
114            $messages_filtered = [];
115            foreach ( $messages_target as $message ) {
116                if ( str_contains( $message, $params['filter'] ) ) {
117                    $messages_filtered[] = $message;
118                }
119            }
120            $messages_target = $messages_filtered;
121        }
122
123        // Whether we have any sort of message customisation filtering
124        $customiseFilterEnabled = $params['customised'] !== 'all';
125        if ( $customiseFilterEnabled ) {
126            $customisedMessages = AllMessagesTablePager::getCustomisedStatuses(
127                array_map(
128                    $langObj->ucfirst( ... ),
129                    $messages_target
130                ),
131                $langObj->getCode(),
132                !$langObj->equals( $this->contentLanguage ),
133                $this->getDB()
134            );
135
136            $customised = $params['customised'] === 'modified';
137        }
138
139        // Get all requested messages and print the result
140        $skip = $params['from'] !== null;
141        $useto = $params['to'] !== null;
142        $result = $this->getResult();
143        foreach ( $messages_target as $message ) {
144            // Skip all messages up to $params['from']
145            if ( $skip && $message === $params['from'] ) {
146                $skip = false;
147            }
148
149            if ( $useto && $message > $params['to'] ) {
150                break;
151            }
152
153            if ( !$skip ) {
154                $a = [
155                    'name' => $message,
156                    'normalizedname' => $this->messageCache->normalizeKey( $message ),
157                ];
158
159                $args = [];
160                if ( isset( $params['args'] ) && count( $params['args'] ) != 0 ) {
161                    $args = $params['args'];
162                }
163
164                if ( $customiseFilterEnabled ) {
165                    $messageIsCustomised = isset( $customisedMessages['pages'][$langObj->ucfirst( $message )] );
166                    // @phan-suppress-next-line PhanPossiblyUndeclaredVariable customised is set when used
167                    if ( $customised === $messageIsCustomised ) {
168                        // @phan-suppress-next-line PhanPossiblyUndeclaredVariable customised is set when used
169                        if ( $customised ) {
170                            $a['customised'] = true;
171                        }
172                    } else {
173                        continue;
174                    }
175                }
176
177                $msg = $this->msg( $message, $args )->inLanguage( $langObj );
178
179                if ( !$msg->exists() ) {
180                    $a['missing'] = true;
181                } else {
182                    // Check if the parser is enabled:
183                    if ( $params['enableparser'] ) {
184                        // @phan-suppress-next-line PhanPossiblyUndeclaredVariable title is set when used
185                        $msgString = $msg->page( $title )->text();
186                    } else {
187                        $msgString = $msg->plain();
188                    }
189                    if ( !$params['nocontent'] ) {
190                        ApiResult::setContentValue( $a, 'content', $msgString );
191                    }
192                    if ( isset( $prop['default'] ) ) {
193                        $default = $this->msg( $message )->inLanguage( $langObj )->useDatabase( false );
194                        if ( !$default->exists() ) {
195                            $a['defaultmissing'] = true;
196                        } elseif ( $default->plain() != $msgString ) {
197                            $a['default'] = $default->plain();
198                        }
199                    }
200                }
201                $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $a );
202                if ( !$fit ) {
203                    $this->setContinueEnumParameter( 'from', $message );
204                    break;
205                }
206            }
207        }
208        $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'message' );
209    }
210
211    /** @inheritDoc */
212    public function getCacheMode( $params ) {
213        if ( $params['lang'] === null ) {
214            // Language not specified, will be fetched from preferences
215            return 'anon-public-user-private';
216        } elseif ( $params['enableparser'] ) {
217            // User-specific parser options will be used
218            return 'anon-public-user-private';
219        } else {
220            // OK to cache
221            return 'public';
222        }
223    }
224
225    /** @inheritDoc */
226    public function getAllowedParams() {
227        return [
228            'messages' => [
229                ParamValidator::PARAM_DEFAULT => '*',
230                ParamValidator::PARAM_ISMULTI => true,
231            ],
232            'prop' => [
233                ParamValidator::PARAM_ISMULTI => true,
234                ParamValidator::PARAM_TYPE => [
235                    'default'
236                ]
237            ],
238            'enableparser' => false,
239            'nocontent' => false,
240            'includelocal' => false,
241            'args' => [
242                ParamValidator::PARAM_ISMULTI => true,
243                ParamValidator::PARAM_ALLOW_DUPLICATES => true,
244            ],
245            'filter' => [],
246            'customised' => [
247                ParamValidator::PARAM_DEFAULT => 'all',
248                ParamValidator::PARAM_TYPE => [
249                    'all',
250                    'modified',
251                    'unmodified'
252                ]
253            ],
254            'lang' => null,
255            'from' => null,
256            'to' => null,
257            'title' => null,
258            'prefix' => null,
259        ];
260    }
261
262    /** @inheritDoc */
263    protected function getExamplesMessages() {
264        return [
265            'action=query&meta=allmessages&amprefix=ipb-'
266                => 'apihelp-query+allmessages-example-ipb',
267            'action=query&meta=allmessages&ammessages=august|mainpage&amlang=de'
268                => 'apihelp-query+allmessages-example-de',
269        ];
270    }
271
272    /** @inheritDoc */
273    public function getHelpUrls() {
274        return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Allmessages';
275    }
276}
277
278/** @deprecated class alias since 1.43 */
279class_alias( ApiQueryAllMessages::class, 'ApiQueryAllMessages' );