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