Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
QueryMessageGroupsActionApi.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\MessageGroupProcessing;
5
7use ApiBase;
8use ApiQuery;
9use ApiQueryBase;
13use MessageGroup;
15use Wikimedia\ParamValidator\ParamValidator;
16
25class QueryMessageGroupsActionApi extends ApiQueryBase {
27 private $hookRunner;
28
29 public function __construct(
30 ApiQuery $query,
31 string $moduleName,
32 HookRunner $hookRunner
33 ) {
34 parent::__construct( $query, $moduleName, 'mg' );
35 $this->hookRunner = $hookRunner;
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 if ( $params['format'] === 'flat' ) {
48 if ( $params['root'] !== '' ) {
49 $group = MessageGroups::getGroup( $params['root'] );
50 if ( $group ) {
51 $groups[$params['root']] = $group;
52 }
53 } else {
55 usort( $groups, [ MessageGroups::class, 'groupLabelSort' ] );
56 }
57 } elseif ( $params['root'] !== '' ) {
58 // format=tree from now on, as it is the only other valid option
59 $group = MessageGroups::getGroup( $params['root'] );
60 if ( $group instanceof AggregateMessageGroup ) {
61 $childIds = [];
62 $groups = MessageGroups::subGroups( $group, $childIds );
63 // The parent group is the first, ignore it
64 array_shift( $groups );
65 }
66 } else {
68 }
69
70 if ( $params['root'] === '' ) {
71 $dynamicGroups = [];
72 foreach ( array_keys( MessageGroups::getDynamicGroups() ) as $id ) {
73 $dynamicGroups[$id] = MessageGroups::getGroup( $id );
74 }
75 // Have dynamic groups appear first in the list
76 $groups = $dynamicGroups + $groups;
77 }
78 '@phan-var (MessageGroup|array)[] $groups';
79
80 // Do not list the sandbox group. The code that knows it
81 // exists can access it directly.
82 if ( isset( $groups['!sandbox'] ) ) {
83 unset( $groups['!sandbox'] );
84 }
85
86 $result = $this->getResult();
87 $matcher = new StringMatcher( '', $filter );
89 foreach ( $groups as $index => $mixed ) {
90 // array when Format = tree
91 $group = is_array( $mixed ) ? reset( $mixed ) : $mixed;
92 if ( $filter !== [] && !$matcher->matches( $group->getId() ) ) {
93 unset( $groups[$index] );
94 continue;
95 }
96
97 if (
98 $params['languageFilter'] !== '' &&
99 TranslateMetadata::isExcluded( $group->getId(), $params['languageFilter'] )
100 ) {
101 unset( $groups[$index] );
102 continue;
103 }
104 }
105
106 if ( $needsMetadata && $groups ) {
107 // FIXME: This doesn't preload subgroups in a tree structure
108 TranslateMetadata::preloadGroups( array_keys( $groups ), __METHOD__ );
109 }
110
112 foreach ( $groups as $index => $mixed ) {
113 $a = $this->formatGroup( $mixed, $props );
114
115 $result->setIndexedTagName( $a, 'group' );
116
117 // @todo Add a continue?
118 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $a );
119 if ( !$fit ) {
120 // Even if we're not going to give a continue, no point carrying on
121 // if the result is full
122 break;
123 }
124 }
125
126 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'group' );
127 }
128
134 protected function formatGroup( $mixed, array $props, int $depth = 0 ): array {
135 $params = $this->extractRequestParams();
136 $context = $this->getContext();
137
138 // Default
139 $g = $mixed;
140 $subgroups = [];
141
142 // Format = tree and has subgroups
143 if ( is_array( $mixed ) ) {
144 $g = array_shift( $mixed );
145 $subgroups = $mixed;
146 }
147
148 $a = [];
149
150 $groupId = $g->getId();
151
152 if ( isset( $props['id'] ) ) {
153 $a['id'] = $groupId;
154 }
155
156 if ( isset( $props['label'] ) ) {
157 $a['label'] = $g->getLabel( $context );
158 }
159
160 if ( isset( $props['description'] ) ) {
161 $a['description'] = $g->getDescription( $context );
162 }
163
164 if ( isset( $props['class'] ) ) {
165 $a['class'] = get_class( $g );
166 }
167
168 if ( isset( $props['namespace'] ) ) {
169 $a['namespace'] = $g->getNamespace();
170 }
171
172 if ( isset( $props['exists'] ) ) {
173 $a['exists'] = $g->exists();
174 }
175
176 if ( isset( $props['icon'] ) ) {
177 $formats = Utilities::getIcon( $g, $params['iconsize'] );
178 if ( $formats ) {
179 $a['icon'] = $formats;
180 }
181 }
182
183 if ( isset( $props['priority'] ) ) {
184 $priority = MessageGroups::getPriority( $g );
185 $a['priority'] = $priority ?: 'default';
186 }
187
188 if ( isset( $props['prioritylangs'] ) ) {
189 $prioritylangs = TranslateMetadata::get( $groupId, 'prioritylangs' );
190 $a['prioritylangs'] = $prioritylangs ? explode( ',', $prioritylangs ) : false;
191 }
192
193 if ( isset( $props['priorityforce'] ) ) {
194 $a['priorityforce'] = ( TranslateMetadata::get( $groupId, 'priorityforce' ) === 'on' );
195 }
196
197 if ( isset( $props['workflowstates'] ) ) {
198 $a['workflowstates'] = $this->getWorkflowStates( $g );
199 }
200
201 if ( isset( $props['sourcelanguage'] ) ) {
202 $a['sourcelanguage'] = $g->getSourceLanguage();
203 }
204
205 $this->hookRunner->onTranslateProcessAPIMessageGroupsProperties(
206 $a, $props, $params, $g
207 );
208
209 // Depth only applies to tree format
210 if ( $depth >= $params['depth'] && $params['format'] === 'tree' ) {
211 $a['groupcount'] = count( $subgroups );
212
213 // Prevent going further down in the three
214 return $a;
215 }
216
217 // Always empty array for flat format, only sometimes for tree format
218 if ( $subgroups !== [] ) {
219 foreach ( $subgroups as $sg ) {
220 $a['groups'][] = $this->formatGroup( $sg, $props );
221 }
222 $result = $this->getResult();
223 $result->setIndexedTagName( $a['groups'], 'group' );
224 }
225
226 return $a;
227 }
228
234 private function getWorkflowStates( MessageGroup $group ) {
235 if ( MessageGroups::isDynamic( $group ) ) {
236 return false;
237 }
238
239 $stateConfig = $group->getMessageGroupStates()->getStates();
240
241 if ( !is_array( $stateConfig ) || $stateConfig === [] ) {
242 return false;
243 }
244
245 $user = $this->getUser();
246
247 foreach ( $stateConfig as $state => $config ) {
248 if ( is_array( $config ) ) {
249 // Check if user is allowed to change states generally
250 $allowed = $user->isAllowed( 'translate-groupreview' );
251 // Check further restrictions
252 if ( $allowed && isset( $config['right'] ) ) {
253 $allowed = $user->isAllowed( $config['right'] );
254 }
255
256 if ( $allowed ) {
257 $stateConfig[$state]['canchange'] = 1;
258 }
259
260 $stateConfig[$state]['name'] =
261 $this->msg( "translate-workflow-state-$state" )->text();
262 }
263 }
264
265 return $stateConfig;
266 }
267
268 protected function getAllowedParams(): array {
269 $allowedParams = [
270 'depth' => [
271 ParamValidator::PARAM_TYPE => 'integer',
272 ParamValidator::PARAM_DEFAULT => 100,
273 ],
274 'filter' => [
275 ParamValidator::PARAM_TYPE => 'string',
276 ParamValidator::PARAM_DEFAULT => '',
277 ParamValidator::PARAM_ISMULTI => true,
278 ],
279 'format' => [
280 ParamValidator::PARAM_TYPE => [ 'flat', 'tree' ],
281 ParamValidator::PARAM_DEFAULT => 'flat',
282 ],
283 'iconsize' => [
284 ParamValidator::PARAM_TYPE => 'integer',
285 ParamValidator::PARAM_DEFAULT => 64,
286 ],
287 'prop' => [
288 ParamValidator::PARAM_TYPE => array_keys( $this->getPropertyList() ),
289 ParamValidator::PARAM_DEFAULT => 'id|label|description|class|exists',
290 ParamValidator::PARAM_ISMULTI => true,
291 ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
292 ],
293 'root' => [
294 ParamValidator::PARAM_TYPE => 'string',
295 ParamValidator::PARAM_DEFAULT => '',
296 ],
297 'languageFilter' => [
298 ParamValidator::PARAM_TYPE => 'string',
299 ParamValidator::PARAM_DEFAULT => '',
300 ]
301 ];
302 $this->hookRunner->onTranslateGetAPIMessageGroupsParameterList( $allowedParams );
303
304 return $allowedParams;
305 }
306
312 private function getPropertyList(): array {
313 $properties = array_flip( [
314 'id',
315 'label',
316 'description',
317 'class',
318 'namespace',
319 'exists',
320 'icon',
321 'priority',
322 'prioritylangs',
323 'priorityforce',
324 'workflowstates',
325 'sourcelanguage'
326 ] );
327
328 $this->hookRunner->onTranslateGetAPIMessageGroupsPropertyDescs( $properties );
329
330 return $properties;
331 }
332
333 protected function getExamplesMessages(): array {
334 return [
335 'action=query&meta=messagegroups' => 'apihelp-query+messagegroups-example-1',
336 ];
337 }
338}
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:FileFormatFactory'=> static function(MediaWikiServices $services):FileFormatFactory { return new FileFormatFactory( $services->getObjectFactory());}, 'Translate:GroupSynchronizationCache'=> static function(MediaWikiServices $services):GroupSynchronizationCache { return new GroupSynchronizationCache( $services->get( 'Translate:PersistentCache'));}, 'Translate:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore($services->get( 'Translate:RevTagStore'), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'));}, 'Translate:MessageGroupReviewStore'=> static function(MediaWikiServices $services):MessageGroupReviewStore { return new MessageGroupReviewStore($services->getDBLoadBalancer(), $services->get( 'Translate:HookRunner'));}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $services->getDBLoadBalancer(), $services->getLinkRenderer(), $services->get( 'Translate:MessageGroupReviewStore'), $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:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore($services->getDBLoadBalancerFactory());}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleExporter'=> static function(MediaWikiServices $services):TranslatableBundleExporter { return new TranslatableBundleExporter($services->get( 'Translate:SubpageListBuilder'), $services->getWikiExporterFactory(), $services->getDBLoadBalancer());}, 'Translate:TranslatableBundleFactory'=> static function(MediaWikiServices $services):TranslatableBundleFactory { return new TranslatableBundleFactory($services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:MessageBundleStore'));}, 'Translate:TranslatableBundleImporter'=> static function(MediaWikiServices $services):TranslatableBundleImporter { return new TranslatableBundleImporter($services->getWikiImporterFactory(), $services->get( 'Translate:TranslatablePageParser'), $services->getRevisionLookup());}, '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(), $services->get( 'Translate:RevTagStore'), $services->getDBLoadBalancer(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'),);}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { $db=$services->getDBLoadBalancer() ->getConnection(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(), $services->getDBLoadBalancer());}, '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.
Hook runner for the Translate extension.
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:31
static get(string $group, string $key)
Get a metadata value for the given group and key.
Interface for message groups.
getIcon()
Returns an icon for this message group if any.
getMessageGroupStates()
Get the message group workflow state configuration.