Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 122
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
JCApi
0.00% covered (danger)
0.00%
0 / 122
0.00% covered (danger)
0.00%
0 / 4
552
0.00% covered (danger)
0.00%
0 / 1
 addStatusConf
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
30
 execute
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
0.00%
0 / 1
272
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2namespace JsonConfig;
3
4use MediaWiki\Api\ApiBase;
5use MediaWiki\Registration\ExtensionRegistry;
6use stdClass;
7use Wikimedia\ParamValidator\ParamValidator;
8
9/**
10 * Allows JsonConfig to be manipulated via API
11 */
12class 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}