Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
QueryMessageGroupsActionApi.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\MessageGroupProcessing;
5
7use ApiQuery;
8use ApiQueryBase;
10use MediaWiki\HookContainer\HookContainer;
11use MessageGroup;
15use Wikimedia\ParamValidator\ParamValidator;
17
26class QueryMessageGroupsActionApi extends ApiQueryBase {
28 private $hookContainer;
29
30 public function __construct(
31 ApiQuery $query,
32 string $moduleName,
33 HookContainer $hookContainer
34 ) {
35 parent::__construct( $query, $moduleName, 'mg' );
36 $this->hookContainer = $hookContainer;
37 }
38
39 public function execute(): void {
40 $params = $this->extractRequestParams();
41 $filter = $params['filter'];
42
43 $groups = [];
44 $props = array_flip( $params['prop'] );
45
46 $needsMetadata = isset( $props['prioritylangs'] ) || isset( $props['priorityforce'] );
47
48 // Parameter root as all for all pages subgroups
49 if ( $params['root'] === 'all' ) {
50 $allGroups = MessageGroups::getAllGroups();
51 foreach ( $allGroups as $id => $group ) {
52 if ( $group instanceof WikiPageMessageGroup ) {
53 $groups[$id] = $group;
54 }
55 }
56 } elseif ( $params['format'] === 'flat' ) {
57 if ( $params['root'] !== '' ) {
58 $group = MessageGroups::getGroup( $params['root'] );
59 if ( $group ) {
60 $groups[$params['root']] = $group;
61 }
62 } else {
63 $groups = MessageGroups::getAllGroups();
64 usort( $groups, [ 'MessageGroups', 'groupLabelSort' ] );
65 }
66 TranslateMetadata::preloadGroups( array_keys( $groups ), __METHOD__ );
67 } elseif ( $params['root'] !== '' ) {
68 // format=tree from now on, as it is the only other valid option
69 $group = MessageGroups::getGroup( $params['root'] );
70 if ( $group instanceof AggregateMessageGroup ) {
71 $childIds = [];
72 $groups = MessageGroups::subGroups( $group, $childIds );
73 // The parent group is the first, ignore it
74 array_shift( $groups );
75 }
76 } else {
77 $groups = MessageGroups::getGroupStructure();
78 }
79
80 if ( $needsMetadata && $groups ) {
81 TranslateMetadata::preloadGroups( array_keys( $groups ), __METHOD__ );
82 }
83
84 if ( $params['root'] === '' ) {
85 $dynamicGroups = [];
86 foreach ( array_keys( MessageGroups::getDynamicGroups() ) as $id ) {
87 $dynamicGroups[$id] = MessageGroups::getGroup( $id );
88 }
89 // Have dynamic groups appear first in the list
90 $groups = $dynamicGroups + $groups;
91 }
92 '@phan-var (MessageGroup|array)[] $groups';
93
94 // Do not list the sandbox group. The code that knows it
95 // exists can access it directly.
96 if ( isset( $groups['!sandbox'] ) ) {
97 unset( $groups['!sandbox'] );
98 }
99
100 $result = $this->getResult();
101 $matcher = new StringMatcher( '', $filter );
103 foreach ( $groups as $mixed ) {
104 // array when Format = tree
105 $group = is_array( $mixed ) ? reset( $mixed ) : $mixed;
106 if ( $filter !== [] && !$matcher->matches( $group->getId() ) ) {
107 continue;
108 }
109
110 if (
111 $params['languageFilter'] !== '' &&
112 TranslateMetadata::isExcluded( $group->getId(), $params['languageFilter'] )
113 ) {
114 continue;
115 }
116
117 $a = $this->formatGroup( $mixed, $props );
118
119 $result->setIndexedTagName( $a, 'group' );
120
121 // @todo Add a continue?
122 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $a );
123 if ( !$fit ) {
124 // Even if we're not going to give a continue, no point carrying on
125 // if the result is full
126 break;
127 }
128 }
129
130 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'group' );
131 }
132
137 protected function formatGroup( $mixed, array $props, int $depth = 0 ): array {
138 $params = $this->extractRequestParams();
139 $context = $this->getContext();
140
141 // Default
142 $g = $mixed;
143 $subgroups = [];
144
145 // Format = tree and has subgroups
146 if ( is_array( $mixed ) ) {
147 $g = array_shift( $mixed );
148 $subgroups = $mixed;
149 }
150
151 $a = [];
152
153 $groupId = $g->getId();
154
155 if ( isset( $props['id'] ) ) {
156 $a['id'] = $groupId;
157 }
158
159 if ( isset( $props['label'] ) ) {
160 $a['label'] = $g->getLabel( $context );
161 }
162
163 if ( isset( $props['description'] ) ) {
164 $a['description'] = $g->getDescription( $context );
165 }
166
167 if ( isset( $props['class'] ) ) {
168 $a['class'] = get_class( $g );
169 }
170
171 if ( isset( $props['namespace'] ) ) {
172 $a['namespace'] = $g->getNamespace();
173 }
174
175 if ( isset( $props['exists'] ) ) {
176 $a['exists'] = $g->exists();
177 }
178
179 if ( isset( $props['icon'] ) ) {
180 $formats = TranslateUtils::getIcon( $g, $params['iconsize'] );
181 if ( $formats ) {
182 $a['icon'] = $formats;
183 }
184 }
185
186 if ( isset( $props['priority'] ) ) {
187 $priority = MessageGroups::getPriority( $g );
188 $a['priority'] = $priority ?: 'default';
189 }
190
191 if ( isset( $props['prioritylangs'] ) ) {
192 $prioritylangs = TranslateMetadata::get( $groupId, 'prioritylangs' );
193 $a['prioritylangs'] = $prioritylangs ? explode( ',', $prioritylangs ) : false;
194 }
195
196 if ( isset( $props['priorityforce'] ) ) {
197 $a['priorityforce'] = ( TranslateMetadata::get( $groupId, 'priorityforce' ) === 'on' );
198 }
199
200 if ( isset( $props['workflowstates'] ) ) {
201 $a['workflowstates'] = $this->getWorkflowStates( $g );
202 }
203
204 if ( isset( $props['sourcelanguage'] ) ) {
205 $a['sourcelanguage'] = $g->getSourceLanguage();
206 }
207
208 $this->hookContainer->run(
209 'TranslateProcessAPIMessageGroupsProperties',
210 [ &$a, $props, $params, $g ]
211 );
212
213 // Depth only applies to tree format
214 if ( $depth >= $params['depth'] && $params['format'] === 'tree' ) {
215 $a['groupcount'] = count( $subgroups );
216
217 // Prevent going further down in the three
218 return $a;
219 }
220
221 // Always empty array for flat format, only sometimes for tree format
222 if ( $subgroups !== [] ) {
223 foreach ( $subgroups as $sg ) {
224 $a['groups'][] = $this->formatGroup( $sg, $props );
225 }
226 $result = $this->getResult();
227 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
228 $result->setIndexedTagName( $a['groups'], 'group' );
229 }
230
231 return $a;
232 }
233
239 private function getWorkflowStates( MessageGroup $group ) {
240 if ( MessageGroups::isDynamic( $group ) ) {
241 return false;
242 }
243
244 $stateConfig = $group->getMessageGroupStates()->getStates();
245
246 if ( !is_array( $stateConfig ) || $stateConfig === [] ) {
247 return false;
248 }
249
250 $user = $this->getUser();
251
252 foreach ( $stateConfig as $state => $config ) {
253 if ( is_array( $config ) ) {
254 // Check if user is allowed to change states generally
255 $allowed = $user->isAllowed( 'translate-groupreview' );
256 // Check further restrictions
257 if ( $allowed && isset( $config['right'] ) ) {
258 $allowed = $user->isAllowed( $config['right'] );
259 }
260
261 if ( $allowed ) {
262 $stateConfig[$state]['canchange'] = 1;
263 }
264
265 $stateConfig[$state]['name'] =
266 $this->msg( "translate-workflow-state-$state" )->text();
267 }
268 }
269
270 return $stateConfig;
271 }
272
273 protected function getAllowedParams(): array {
274 $allowedParams = [
275 'depth' => [
276 ParamValidator::PARAM_TYPE => 'integer',
277 ParamValidator::PARAM_DEFAULT => 100,
278 ],
279 'filter' => [
280 ParamValidator::PARAM_TYPE => 'string',
281 ParamValidator::PARAM_DEFAULT => '',
282 ParamValidator::PARAM_ISMULTI => true,
283 ],
284 'format' => [
285 ParamValidator::PARAM_TYPE => [ 'flat', 'tree' ],
286 ParamValidator::PARAM_DEFAULT => 'flat',
287 ],
288 'iconsize' => [
289 ParamValidator::PARAM_TYPE => 'integer',
290 ParamValidator::PARAM_DEFAULT => 64,
291 ],
292 'prop' => [
293 ParamValidator::PARAM_TYPE => array_keys( $this->getPropertyList() ),
294 ParamValidator::PARAM_DEFAULT => 'id|label|description|class|exists',
295 ParamValidator::PARAM_ISMULTI => true,
296 ],
297 'root' => [
298 ParamValidator::PARAM_TYPE => 'string',
299 ParamValidator::PARAM_DEFAULT => '',
300 ],
301 'languageFilter' => [
302 ParamValidator::PARAM_TYPE => 'string',
303 ParamValidator::PARAM_DEFAULT => '',
304 ]
305 ];
306 $this->hookContainer->run( 'TranslateGetAPIMessageGroupsParameterList', [ &$allowedParams ] );
307
308 return $allowedParams;
309 }
310
316 private function getPropertyList(): array {
317 $properties = array_flip( [
318 'id',
319 'label',
320 'description',
321 'class',
322 'namespace',
323 'exists',
324 'icon',
325 'priority',
326 'prioritylangs',
327 'priorityforce',
328 'workflowstates',
329 'sourcelanguage'
330 ] );
331
332 $this->hookContainer->run( 'TranslateGetAPIMessageGroupsPropertyDescs', [ &$properties ] );
333
334 return $properties;
335 }
336
337 protected function getExamplesMessages(): array {
338 return [
339 'action=query&meta=messagegroups' => 'apihelp-query+messagegroups-example-1',
340 ];
341 }
342}
return[ 'Translate:ConfigHelper'=> static function():ConfigHelper { return new ConfigHelper();}, 'Translate:CsvTranslationImporter'=> static function(MediaWikiServices $services):CsvTranslationImporter { return new CsvTranslationImporter( $services->getWikiPageFactory());}, 'Translate:EntitySearch'=> static function(MediaWikiServices $services):EntitySearch { return new EntitySearch($services->getMainWANObjectCache(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), MessageGroups::singleton(), $services->getNamespaceInfo(), $services->get( 'Translate:MessageIndex'), $services->getTitleParser(), $services->getTitleFormatter());}, 'Translate:ExternalMessageSourceStateImporter'=> static function(MediaWikiServices $services):ExternalMessageSourceStateImporter { return new ExternalMessageSourceStateImporter($services->getMainConfig(), $services->get( 'Translate:GroupSynchronizationCache'), $services->getJobQueueGroup(), LoggerFactory::getInstance( 'Translate.GroupSynchronization'), MessageIndex::singleton());}, 'Translate:GroupSynchronizationCache'=> static function(MediaWikiServices $services):GroupSynchronizationCache { return new GroupSynchronizationCache( $services->get( 'Translate:PersistentCache'));}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore(new RevTagStore(), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'));}, 'Translate:MessageGroupReview'=> static function(MediaWikiServices $services):MessageGroupReview { return new MessageGroupReview($services->getDBLoadBalancer(), $services->getHookContainer());}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=$services->getMainConfig() ->get( 'TranslateMessageIndex');if(is_string( $params)) { $params=(array) $params;} $class=array_shift( $params);return new $class( $params);}, 'Translate:ParsingPlaceholderFactory'=> static function():ParsingPlaceholderFactory { return new ParsingPlaceholderFactory();}, 'Translate:PersistentCache'=> static function(MediaWikiServices $services):PersistentCache { return new PersistentDatabaseCache($services->getDBLoadBalancer(), $services->getJsonCodec());}, 'Translate:ProgressStatsTableFactory'=> static function(MediaWikiServices $services):ProgressStatsTableFactory { return new ProgressStatsTableFactory($services->getLinkRenderer(), $services->get( 'Translate:ConfigHelper'));}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleFactory'=> static function(MediaWikiServices $services):TranslatableBundleFactory { return new TranslatableBundleFactory($services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:MessageBundleStore'));}, 'Translate:TranslatableBundleMover'=> static function(MediaWikiServices $services):TranslatableBundleMover { return new TranslatableBundleMover($services->getMovePageFactory(), $services->getJobQueueGroup(), $services->getLinkBatchFactory(), $services->get( 'Translate:TranslatableBundleFactory'), $services->get( 'Translate:SubpageListBuilder'), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatablePageParser'=> static function(MediaWikiServices $services):TranslatablePageParser { return new TranslatablePageParser($services->get( 'Translate:ParsingPlaceholderFactory'));}, 'Translate:TranslatablePageStore'=> static function(MediaWikiServices $services):TranslatablePageStore { return new TranslatablePageStore($services->get( 'Translate:MessageIndex'), $services->getJobQueueGroup(), new RevTagStore(), $services->getDBLoadBalancer());}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { $db=$services->getDBLoadBalancer() ->getConnectionRef(DB_REPLICA);return new TranslationStashStorage( $db);}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory());}, 'Translate:TranslationUnitStoreFactory'=> static function(MediaWikiServices $services):TranslationUnitStoreFactory { return new TranslationUnitStoreFactory( $services->getDBLoadBalancer());}, 'Translate:TranslatorActivity'=> static function(MediaWikiServices $services):TranslatorActivity { $query=new TranslatorActivityQuery($services->getMainConfig(), $services->getDBLoadBalancer());return new TranslatorActivity($services->getMainObjectStash(), $query, $services->getJobQueueGroup());}, 'Translate:TtmServerFactory'=> static function(MediaWikiServices $services):TtmServerFactory { $config=$services->getMainConfig();$default=$config->get( 'TranslateTranslationDefaultService');if( $default===false) { $default=null;} return new TtmServerFactory( $config->get( 'TranslateTranslationServices'), $default);}]
@phpcs-require-sorted-array
Groups multiple message groups together as one group.
The versatile default implementation of StringMangler interface.
Factory class for accessing message groups individually by id or all of them as an list.
static getPriority( $group)
We want to de-emphasize time sensitive groups like news for 2009.
static get( $group, $key)
Get a metadata value for the given group and key.
Essentially random collection of helper functions, similar to GlobalFunctions.php.
static getIcon(MessageGroup $g, $size)
Get URLs for icons if available.
Wraps the translatable page sections into a message group.
Interface for message groups.
getMessageGroupStates()
Get the message group workflow state configuration.