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