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