Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
108 / 108 |
|
100.00% |
6 / 6 |
CRAP | |
100.00% |
1 / 1 |
SubmoduleDef | |
100.00% |
108 / 108 |
|
100.00% |
6 / 6 |
42 | |
100.00% |
1 / 1 |
checkSettings | |
100.00% |
25 / 25 |
|
100.00% |
1 / 1 |
8 | |||
getEnumValues | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getParamInfo | |
100.00% |
35 / 35 |
|
100.00% |
1 / 1 |
13 | |||
getSubmoduleMap | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
sortEnumValues | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
8 | |||
getEnumValuesForHelp | |
100.00% |
23 / 23 |
|
100.00% |
1 / 1 |
7 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Api\Validator; |
4 | |
5 | use MediaWiki\Api\ApiBase; |
6 | use MediaWiki\Api\ApiUsageException; |
7 | use MediaWiki\Html\Html; |
8 | use Wikimedia\ParamValidator\TypeDef\EnumDef; |
9 | |
10 | /** |
11 | * Type definition for submodule types |
12 | * |
13 | * A submodule type is an enum type for selecting Action API submodules. |
14 | * |
15 | * @since 1.35 |
16 | */ |
17 | class SubmoduleDef extends EnumDef { |
18 | |
19 | /** |
20 | * (string[]) Map parameter values to submodule paths. |
21 | * |
22 | * Default is to use all modules in $options['module']->getModuleManager() |
23 | * in the group matching the parameter name. |
24 | */ |
25 | public const PARAM_SUBMODULE_MAP = 'param-submodule-map'; |
26 | |
27 | /** |
28 | * (string) Used to indicate the 'g' prefix added by ApiQueryGeneratorBase |
29 | * (and similar if anything else ever does that). |
30 | */ |
31 | public const PARAM_SUBMODULE_PARAM_PREFIX = 'param-submodule-param-prefix'; |
32 | |
33 | public function checkSettings( string $name, $settings, array $options, array $ret ): array { |
34 | $map = $settings[self::PARAM_SUBMODULE_MAP] ?? []; |
35 | if ( !is_array( $map ) ) { |
36 | $ret['issues'][self::PARAM_SUBMODULE_MAP] = 'PARAM_SUBMODULE_MAP must be an array, got ' |
37 | . gettype( $map ); |
38 | // Prevent errors in parent::checkSettings() |
39 | $settings[self::PARAM_SUBMODULE_MAP] = null; |
40 | } |
41 | |
42 | $ret = parent::checkSettings( $name, $settings, $options, $ret ); |
43 | |
44 | $ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [ |
45 | self::PARAM_SUBMODULE_MAP, self::PARAM_SUBMODULE_PARAM_PREFIX, |
46 | ] ); |
47 | |
48 | if ( is_array( $map ) ) { |
49 | $module = $options['module']; |
50 | foreach ( $map as $k => $v ) { |
51 | if ( !is_string( $v ) ) { |
52 | $ret['issues'][] = 'Values for PARAM_SUBMODULE_MAP must be strings, ' |
53 | . "but value for \"$k\" is " . gettype( $v ); |
54 | continue; |
55 | } |
56 | |
57 | try { |
58 | $submod = $module->getModuleFromPath( $v ); |
59 | } catch ( ApiUsageException $ex ) { |
60 | $submod = null; |
61 | } |
62 | if ( !$submod ) { |
63 | $ret['issues'][] = "PARAM_SUBMODULE_MAP contains \"$v\", which is not a valid module path"; |
64 | } |
65 | } |
66 | } |
67 | |
68 | if ( !is_string( $settings[self::PARAM_SUBMODULE_PARAM_PREFIX] ?? '' ) ) { |
69 | $ret['issues'][self::PARAM_SUBMODULE_PARAM_PREFIX] = 'PARAM_SUBMODULE_PARAM_PREFIX must be ' |
70 | . 'a string, got ' . gettype( $settings[self::PARAM_SUBMODULE_PARAM_PREFIX] ); |
71 | } |
72 | |
73 | return $ret; |
74 | } |
75 | |
76 | public function getEnumValues( $name, array $settings, array $options ) { |
77 | if ( isset( $settings[self::PARAM_SUBMODULE_MAP] ) ) { |
78 | $modules = array_keys( $settings[self::PARAM_SUBMODULE_MAP] ); |
79 | } else { |
80 | $modules = $options['module']->getModuleManager()->getNames( $name ); |
81 | } |
82 | |
83 | return $modules; |
84 | } |
85 | |
86 | public function getParamInfo( $name, array $settings, array $options ) { |
87 | $info = parent::getParamInfo( $name, $settings, $options ); |
88 | /** @var ApiBase $module */ |
89 | $module = $options['module']; |
90 | |
91 | if ( isset( $settings[self::PARAM_SUBMODULE_MAP] ) ) { |
92 | $info['type'] = array_keys( $settings[self::PARAM_SUBMODULE_MAP] ); |
93 | $info['submodules'] = $settings[self::PARAM_SUBMODULE_MAP]; |
94 | } else { |
95 | $info['type'] = $module->getModuleManager()->getNames( $name ); |
96 | $prefix = $module->isMain() ? '' : ( $module->getModulePath() . '+' ); |
97 | $info['submodules'] = []; |
98 | foreach ( $info['type'] as $v ) { |
99 | $info['submodules'][$v] = $prefix . $v; |
100 | } |
101 | } |
102 | if ( isset( $settings[self::PARAM_SUBMODULE_PARAM_PREFIX] ) ) { |
103 | $info['submoduleparamprefix'] = $settings[self::PARAM_SUBMODULE_PARAM_PREFIX]; |
104 | } |
105 | |
106 | $submoduleFlags = []; // for sorting: higher flags are sorted later |
107 | $submoduleNames = []; // for sorting: lexicographical, ascending |
108 | foreach ( $info['submodules'] as $v => $submodulePath ) { |
109 | try { |
110 | $submod = $module->getModuleFromPath( $submodulePath ); |
111 | } catch ( ApiUsageException $ex ) { |
112 | $submoduleFlags[] = 0; |
113 | $submoduleNames[] = $v; |
114 | continue; |
115 | } |
116 | $flags = 0; |
117 | if ( $submod && $submod->isDeprecated() ) { |
118 | $info['deprecatedvalues'][] = $v; |
119 | $flags |= 1; |
120 | } |
121 | if ( $submod && $submod->isInternal() ) { |
122 | $info['internalvalues'][] = $v; |
123 | $flags |= 2; |
124 | } |
125 | $submoduleFlags[] = $flags; |
126 | $submoduleNames[] = $v; |
127 | } |
128 | // sort $info['submodules'] and $info['type'] by $submoduleFlags and $submoduleNames |
129 | array_multisort( $submoduleFlags, $submoduleNames, $info['submodules'], $info['type'] ); |
130 | if ( isset( $info['deprecatedvalues'] ) ) { |
131 | sort( $info['deprecatedvalues'] ); |
132 | } |
133 | if ( isset( $info['internalvalues'] ) ) { |
134 | sort( $info['internalvalues'] ); |
135 | } |
136 | |
137 | return $info; |
138 | } |
139 | |
140 | private function getSubmoduleMap( ApiBase $module, string $name, array $settings ): array { |
141 | if ( isset( $settings[self::PARAM_SUBMODULE_MAP] ) ) { |
142 | $map = $settings[self::PARAM_SUBMODULE_MAP]; |
143 | } else { |
144 | $prefix = $module->isMain() ? '' : ( $module->getModulePath() . '+' ); |
145 | $map = []; |
146 | foreach ( $module->getModuleManager()->getNames( $name ) as $submoduleName ) { |
147 | $map[$submoduleName] = $prefix . $submoduleName; |
148 | } |
149 | } |
150 | |
151 | return $map; |
152 | } |
153 | |
154 | protected function sortEnumValues( |
155 | string $name, array $values, array $settings, array $options |
156 | ): array { |
157 | $module = $options['module']; |
158 | $map = $this->getSubmoduleMap( $module, $name, $settings ); |
159 | |
160 | $submoduleFlags = []; // for sorting: higher flags are sorted later |
161 | foreach ( $values as $k => $v ) { |
162 | $flags = 0; |
163 | try { |
164 | $submod = isset( $map[$v] ) ? $module->getModuleFromPath( $map[$v] ) : null; |
165 | if ( $submod && $submod->isDeprecated() ) { |
166 | $flags |= 1; |
167 | } |
168 | if ( $submod && $submod->isInternal() ) { |
169 | $flags |= 2; |
170 | } |
171 | } catch ( ApiUsageException $ex ) { |
172 | // Ignore |
173 | } |
174 | $submoduleFlags[$k] = $flags; |
175 | } |
176 | array_multisort( $submoduleFlags, $values, SORT_NATURAL ); |
177 | |
178 | return $values; |
179 | } |
180 | |
181 | protected function getEnumValuesForHelp( $name, array $settings, array $options ) { |
182 | $module = $options['module']; |
183 | $map = $this->getSubmoduleMap( $module, $name, $settings ); |
184 | $defaultAttrs = [ 'dir' => 'ltr', 'lang' => 'en' ]; |
185 | |
186 | $values = []; |
187 | $submoduleFlags = []; // for sorting: higher flags are sorted later |
188 | $submoduleNames = []; // for sorting: lexicographical, ascending |
189 | foreach ( $map as $v => $m ) { |
190 | $attrs = $defaultAttrs; |
191 | $flags = 0; |
192 | try { |
193 | $submod = $module->getModuleFromPath( $m ); |
194 | if ( $submod && $submod->isDeprecated() ) { |
195 | $attrs['class'][] = 'apihelp-deprecated-value'; |
196 | $flags |= 1; |
197 | } |
198 | if ( $submod && $submod->isInternal() ) { |
199 | $attrs['class'][] = 'apihelp-internal-value'; |
200 | $flags |= 2; |
201 | } |
202 | } catch ( ApiUsageException $ex ) { |
203 | // Ignore |
204 | } |
205 | $v = Html::element( 'span', $attrs, $v ); |
206 | $values[] = "[[Special:ApiHelp/{$m}|{$v}]]"; |
207 | $submoduleFlags[] = $flags; |
208 | $submoduleNames[] = $v; |
209 | } |
210 | // sort $values by $submoduleFlags and $submoduleNames |
211 | array_multisort( $submoduleFlags, $submoduleNames, SORT_NATURAL, $values, SORT_NATURAL ); |
212 | |
213 | return $values; |
214 | } |
215 | |
216 | } |