Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 84 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
RemoteMessageContentFetcher | |
0.00% |
0 / 84 |
|
0.00% |
0 / 4 |
132 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getContent | |
0.00% |
0 / 40 |
|
0.00% |
0 / 1 |
20 | |||
parseQueryApiResponse | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
20 | |||
getApiEndpoint | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\MassMessage\MessageContentFetcher; |
5 | |
6 | use MediaWiki\Config\SiteConfiguration; |
7 | use MediaWiki\Http\HttpRequestFactory; |
8 | use MediaWiki\MassMessage\LanguageAwareText; |
9 | use MediaWiki\MediaWikiServices; |
10 | use MediaWiki\Status\Status; |
11 | |
12 | /** |
13 | * Fetches content from a remote wiki |
14 | * @author Abijeet Patro |
15 | * @since 2022.01 |
16 | * @license GPL-2.0-or-later |
17 | */ |
18 | class RemoteMessageContentFetcher { |
19 | /** @var HttpRequestFactory */ |
20 | private $httpRequestFactory; |
21 | /** @var SiteConfiguration */ |
22 | private $siteConfiguration; |
23 | |
24 | /** |
25 | * @param HttpRequestFactory $requestFactory |
26 | * @param SiteConfiguration $siteConfiguration |
27 | */ |
28 | public function __construct( HttpRequestFactory $requestFactory, SiteConfiguration $siteConfiguration ) { |
29 | $this->httpRequestFactory = $requestFactory; |
30 | $this->siteConfiguration = $siteConfiguration; |
31 | } |
32 | |
33 | /** |
34 | * Fetch remote content and return Status object. The Status value contains a LanguageAwareText object |
35 | * @param string $pageTitle |
36 | * @param string $wikiId |
37 | * @return Status |
38 | */ |
39 | public function getContent( string $pageTitle, string $wikiId ): Status { |
40 | $apiUrl = $this->getApiEndpoint( $wikiId ); |
41 | if ( !$apiUrl ) { |
42 | return Status::newFatal( |
43 | 'massmessage-page-message-wiki-not-found', |
44 | $wikiId, |
45 | $pageTitle |
46 | ); |
47 | } |
48 | |
49 | $queryParams = [ |
50 | 'action' => 'query', |
51 | 'format' => 'json', |
52 | 'prop' => 'info|revisions', |
53 | 'rvprop' => 'content', |
54 | 'rvslots' => 'main', |
55 | 'titles' => $pageTitle, |
56 | 'formatversion' => 2, |
57 | ]; |
58 | |
59 | $options = [ |
60 | 'method' => 'GET', |
61 | 'timeout' => 15, |
62 | ]; |
63 | |
64 | $apiUrl .= '?' . http_build_query( $queryParams ); |
65 | $req = $this->httpRequestFactory->create( $apiUrl, $options, __METHOD__ ); |
66 | |
67 | $status = $req->execute(); |
68 | if ( !$status->isOK() ) { |
69 | // FIXME: Formatting is broken here, needs to be improved. |
70 | return Status::newFatal( |
71 | "massmessage-page-message-fetch-error-in-wiki", |
72 | $wikiId, |
73 | $pageTitle, |
74 | $status->getMessage()->text() |
75 | ); |
76 | } |
77 | |
78 | $json = $req->getContent(); |
79 | $response = json_decode( $json, true ); |
80 | if ( $response === null ) { |
81 | return Status::newFatal( |
82 | "massmessage-page-message-parsing-error-in-wiki", |
83 | $wikiId, |
84 | $pageTitle, |
85 | json_last_error_msg() |
86 | ); |
87 | } |
88 | |
89 | return $this->parseQueryApiResponse( $response, $wikiId, $pageTitle, $json ); |
90 | } |
91 | |
92 | /** |
93 | * @param array $response |
94 | * @param string $wikiId |
95 | * @param string $pageTitle |
96 | * @param string $json |
97 | * @return Status |
98 | */ |
99 | private function parseQueryApiResponse( |
100 | array $response, |
101 | string $wikiId, |
102 | string $pageTitle, |
103 | string $json |
104 | ): Status { |
105 | // Example response: |
106 | // { |
107 | // "batchcomplete": true, |
108 | // "query": { |
109 | // "pages": [ { |
110 | // "pageid": 11285354, |
111 | // "ns": 0, |
112 | // "title": "Tech/News/2021/12", |
113 | // "contentmodel": "wikitext", |
114 | // "pagelanguage": "en", |
115 | // "pagelanguagehtmlcode": "en", |
116 | // "pagelanguagedir": "ltr", |
117 | // "touched": "2021-03-23T06:05:06Z", |
118 | // "lastrevid": 21247464, |
119 | // "length": 4585, |
120 | // "revisions": [ { |
121 | // "slots": { |
122 | // "main": { |
123 | // "contentmodel": "wikitext", |
124 | // "contentformat": "text/x-wiki", |
125 | // "content": "[...]" |
126 | // } |
127 | // } |
128 | // } ] |
129 | // } ] |
130 | // } |
131 | // } |
132 | |
133 | $pages = $response['query']['pages'] ?? []; |
134 | if ( isset( $response['error']['info'] ) || count( $pages ) !== 1 ) { |
135 | return Status::newFatal( |
136 | 'massmessage-page-message-parse-invalid-in-wiki', |
137 | $wikiId, |
138 | $pageTitle, |
139 | $response['error']['info'] ?? $json |
140 | ); |
141 | } |
142 | |
143 | // Take first and only one out of the list |
144 | $page = current( $pages ); |
145 | |
146 | if ( isset( $page['missing'] ) ) { |
147 | // Page was not found |
148 | return Status::newFatal( |
149 | 'massmessage-page-message-not-found-in-wiki', |
150 | $wikiId, |
151 | $pageTitle |
152 | ); |
153 | } |
154 | |
155 | $content = new LanguageAwareText( |
156 | $page['revisions'][0]['slots']['main']['content'], |
157 | $page['pagelanguage'], |
158 | $page['pagelanguagedir'] |
159 | ); |
160 | |
161 | return Status::newGood( $content ); |
162 | } |
163 | |
164 | /** |
165 | * @param string $wiki |
166 | * @return string|null |
167 | */ |
168 | private function getApiEndpoint( string $wiki ): ?string { |
169 | $this->siteConfiguration->loadFullData(); |
170 | |
171 | $siteFromDB = $this->siteConfiguration->siteFromDB( $wiki ); |
172 | [ $major, $minor ] = $siteFromDB; |
173 | |
174 | if ( $major === null ) { |
175 | return null; |
176 | } |
177 | |
178 | $configOpts = [ 'lang' => $minor, 'site' => $major ]; |
179 | $server = $this->siteConfiguration->get( |
180 | 'wgServer', |
181 | $wiki, |
182 | null, |
183 | $configOpts |
184 | ); |
185 | $scriptPath = $this->siteConfiguration->get( |
186 | 'wgScriptPath', |
187 | $wiki, |
188 | null, |
189 | $configOpts |
190 | ); |
191 | |
192 | $urlUtils = MediaWikiServices::getInstance()->getUrlUtils(); |
193 | $apiPath = $urlUtils->expand( $server . $scriptPath . '/api.php', PROTO_INTERNAL ); |
194 | |
195 | return $apiPath; |
196 | } |
197 | } |