Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 122 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
JCApi | |
0.00% |
0 / 122 |
|
0.00% |
0 / 4 |
552 | |
0.00% |
0 / 1 |
addStatusConf | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
30 | |||
execute | |
0.00% |
0 / 65 |
|
0.00% |
0 / 1 |
272 | |||
getAllowedParams | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
2 | |||
getExamplesMessages | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | namespace JsonConfig; |
3 | |
4 | use MediaWiki\Api\ApiBase; |
5 | use MediaWiki\Registration\ExtensionRegistry; |
6 | use stdClass; |
7 | use Wikimedia\ParamValidator\ParamValidator; |
8 | |
9 | /** |
10 | * Allows JsonConfig to be manipulated via API |
11 | */ |
12 | class JCApi extends ApiBase { |
13 | |
14 | /** |
15 | * @param stdClass $conf |
16 | * @return array |
17 | */ |
18 | private static function addStatusConf( $conf ) { |
19 | // explicitly list values to avoid accidental exposure of private data |
20 | $res = [ |
21 | 'model' => $conf->model, |
22 | 'namespace' => $conf->namespace, |
23 | 'nsName' => $conf->nsName, |
24 | 'nsTalk' => ( $conf->nsTalk ?? '' ) ?: 'default', |
25 | 'isLocal' => $conf->isLocal, |
26 | 'cacheExp' => $conf->cacheExp, |
27 | 'cacheKey' => $conf->cacheKey, |
28 | 'flaggedRevs' => $conf->flaggedRevs, |
29 | ]; |
30 | $remote = $conf->remote ?? null; |
31 | if ( $remote ) { |
32 | $res['remote'] = [ |
33 | 'url' => $remote->url, |
34 | 'username' => $remote->username !== '', // true or false |
35 | 'password' => $remote->password !== '', // true or false |
36 | ]; |
37 | } |
38 | $store = $conf->store ?? null; |
39 | if ( $store === false ) { |
40 | // when store equals to false it's a flag to indicate content is stored externaly |
41 | $res['store'] = [ |
42 | 'cacheNewValue' => null, |
43 | 'notifyUrl' => null, |
44 | 'notifyUsername' => false, |
45 | 'notifyPassword' => false, |
46 | ]; |
47 | } elseif ( $store ) { |
48 | $res['store'] = [ |
49 | 'cacheNewValue' => $store->cacheNewValue, |
50 | 'notifyUrl' => $store->notifyUrl, |
51 | 'notifyUsername' => $store->notifyUsername !== '', |
52 | 'notifyPassword' => $store->notifyPassword !== '', |
53 | ]; |
54 | } |
55 | return $res; |
56 | } |
57 | |
58 | public function execute() { |
59 | $result = $this->getResult(); |
60 | |
61 | $params = $this->extractRequestParams(); |
62 | $command = $params['command']; |
63 | |
64 | switch ( $command ) { |
65 | case 'status': |
66 | $this->getMain()->setCacheMaxAge( 1 * 30 ); // seconds |
67 | $this->getMain()->setCacheMode( 'public' ); |
68 | |
69 | $result->addValue( |
70 | null, |
71 | 'models', |
72 | ExtensionRegistry::getInstance()->getAttribute( 'JsonConfigModels' ) |
73 | + $this->getConfig()->get( 'JsonConfigModels' ) |
74 | ); |
75 | |
76 | $data = []; |
77 | foreach ( JCSingleton::getTitleMap() as $ns => $confs ) { |
78 | $vals = []; |
79 | foreach ( $confs as $conf ) { |
80 | $vals[] = self::addStatusConf( $conf ); |
81 | } |
82 | $data[$ns] = $vals; |
83 | } |
84 | if ( $data ) { |
85 | $result->setIndexedTagName( $data, 'ns' ); |
86 | } |
87 | $result->addValue( null, 'titleMap', $data ); |
88 | break; |
89 | |
90 | case 'reset': |
91 | case 'reload': |
92 | // FIXME: this should be POSTed, not GETed. |
93 | // This code should match JCSingleton::onArticleChangeComplete() |
94 | // Currently, that action is not used because in production store->notifyUrl is null |
95 | // Can MW API allow both for the same action, or should it be a separate action? |
96 | |
97 | $this->getMain()->setCacheMaxAge( 1 ); // seconds |
98 | $this->getMain()->setCacheMode( 'private' ); |
99 | if ( !$this->getUser()->isAllowed( 'jsonconfig-flush' ) ) { |
100 | // Sigh. Can't use $this->checkUserRightsAny() because |
101 | // this has to break API conventions by returning 401 |
102 | // (and violate the HTTP RFC by doing so without a |
103 | // WWW-Authenticate header). |
104 | $this->dieWithError( |
105 | [ |
106 | 'apierror-permissiondenied', |
107 | $this->msg( "action-jsonconfig-flush" ) |
108 | ], |
109 | 'permissiondenied', [], 401 |
110 | ); |
111 | } |
112 | if ( !isset( $params['namespace'] ) ) { |
113 | $this->dieWithError( |
114 | [ 'apierror-jsonconfig-paramrequired', 'namespace' ], |
115 | 'badparam-namespace' |
116 | ); |
117 | } |
118 | if ( !isset( $params['title'] ) ) { |
119 | $this->dieWithError( |
120 | [ 'apierror-jsonconfig-paramrequired', 'title' ], 'badparam-title' |
121 | ); |
122 | } |
123 | |
124 | $jct = JCSingleton::parseTitle( $params['title'], $params['namespace'] ); |
125 | if ( !$jct ) { |
126 | $this->dieWithError( 'apierror-jsonconfig-badtitle', 'badparam-titles' ); |
127 | } |
128 | |
129 | if ( isset( $params['content'] ) && $params['content'] !== '' ) { |
130 | if ( $command !== 'reload ' ) { |
131 | $this->dieWithError( |
132 | [ |
133 | 'apierror-invalidparammix-mustusewith', |
134 | 'content', |
135 | 'command=reload' |
136 | ], |
137 | 'badparam-content' |
138 | ); |
139 | } |
140 | $content = JCSingleton::parseContent( $jct, $params['content'], true ); |
141 | } else { |
142 | $content = false; |
143 | } |
144 | |
145 | $jc = new JCCache( $jct, $content ); |
146 | if ( $command === 'reset' ) { |
147 | $jc->resetCache( false ); // clear cache |
148 | } elseif ( $content ) { |
149 | $jc->resetCache( true ); // set new value in cache |
150 | } else { |
151 | $jc->get(); // gets content from the default source and cache |
152 | } |
153 | |
154 | break; |
155 | } |
156 | } |
157 | |
158 | public function getAllowedParams() { |
159 | return [ |
160 | 'command' => [ |
161 | ParamValidator::PARAM_DEFAULT => 'status', |
162 | ParamValidator::PARAM_TYPE => [ |
163 | 'status', |
164 | 'reset', |
165 | 'reload', |
166 | ], |
167 | ApiBase::PARAM_HELP_MSG_PER_VALUE => [], |
168 | ], |
169 | 'namespace' => [ |
170 | ParamValidator::PARAM_TYPE => 'integer', |
171 | ], |
172 | 'title' => '', |
173 | 'content' => '', |
174 | ]; |
175 | } |
176 | |
177 | /** |
178 | * @see ApiBase::getExamplesMessages() |
179 | * @return array |
180 | */ |
181 | protected function getExamplesMessages() { |
182 | return [ |
183 | 'action=jsonconfig&format=json' |
184 | => 'apihelp-jsonconfig-example-1', |
185 | 'action=jsonconfig&command=reset&namespace=486&title=Brazil/Amazonas.map&format=json' |
186 | => 'apihelp-jsonconfig-example-2', |
187 | 'action=jsonconfig&command=reload&namespace=486&title=Brazil/Amazonas.map&format=json' |
188 | => 'apihelp-jsonconfig-example-3', |
189 | ]; |
190 | } |
191 | } |