Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
76.92% |
50 / 65 |
|
66.67% |
2 / 3 |
CRAP | |
0.00% |
0 / 1 |
GlobalBlockReasonFormatter | |
76.92% |
50 / 65 |
|
66.67% |
2 / 3 |
10.00 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
format | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
2 | |||
expandRemoteTemplates | |
100.00% |
45 / 45 |
|
100.00% |
1 / 1 |
7 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | namespace MediaWiki\Extension\GlobalBlocking\Services; |
22 | |
23 | use FormatJson; |
24 | use MediaWiki\Config\ServiceOptions; |
25 | use MediaWiki\Http\HttpRequestFactory; |
26 | use Psr\Log\LoggerInterface; |
27 | use RuntimeException; |
28 | use WANObjectCache; |
29 | |
30 | /** |
31 | * @author Taavi "Majavah" Väänänen <hi@taavi.wtf> |
32 | */ |
33 | class GlobalBlockReasonFormatter { |
34 | private const CACHE_TTL = 3600; |
35 | private const CACHE_VERSION = 0; |
36 | |
37 | /** @internal Only public for service wiring use. */ |
38 | public const CONSTRUCTOR_OPTIONS = [ |
39 | 'GlobalBlockRemoteReasonUrl', |
40 | ]; |
41 | |
42 | /** @var ServiceOptions */ |
43 | private $options; |
44 | |
45 | /** @var WANObjectCache */ |
46 | private $wanObjectCache; |
47 | |
48 | /** @var HttpRequestFactory */ |
49 | private $httpRequestFactory; |
50 | |
51 | /** @var LoggerInterface */ |
52 | private $logger; |
53 | |
54 | /** |
55 | * @param ServiceOptions $options |
56 | * @param WANObjectCache $wanObjectCache |
57 | * @param HttpRequestFactory $httpRequestFactory |
58 | * @param LoggerInterface $logger |
59 | */ |
60 | public function __construct( |
61 | ServiceOptions $options, |
62 | WANObjectCache $wanObjectCache, |
63 | HttpRequestFactory $httpRequestFactory, |
64 | LoggerInterface $logger |
65 | ) { |
66 | $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); |
67 | $this->options = $options; |
68 | $this->wanObjectCache = $wanObjectCache; |
69 | $this->httpRequestFactory = $httpRequestFactory; |
70 | $this->logger = $logger; |
71 | } |
72 | |
73 | /** |
74 | * @param string $wikitext |
75 | * @param string $langCode |
76 | * @return string |
77 | */ |
78 | public function format( string $wikitext, string $langCode ): string { |
79 | $cacheKey = $this->wanObjectCache->makeGlobalKey( |
80 | 'GlobalBlocking', |
81 | 'BlockReason', |
82 | $langCode, |
83 | sha1( $this->options->get( 'GlobalBlockRemoteReasonUrl' ) ?? 'local' ), |
84 | sha1( $wikitext ) |
85 | ); |
86 | |
87 | // TODO: does this need poolcounter support? |
88 | |
89 | return $this->wanObjectCache->getWithSetCallback( |
90 | $cacheKey, |
91 | self::CACHE_TTL, |
92 | function ( $oldValue, &$ttl, array &$setOpts ) use ( $wikitext, $langCode ) { |
93 | return $this->expandRemoteTemplates( $wikitext, $langCode ); |
94 | }, |
95 | [ 'version' => self::CACHE_VERSION ] |
96 | ); |
97 | } |
98 | |
99 | /** |
100 | * @param string $wikitext |
101 | * @param string $langCode |
102 | * @return string |
103 | */ |
104 | private function expandRemoteTemplates( string $wikitext, string $langCode ): string { |
105 | $url = $this->options->get( 'GlobalBlockRemoteReasonUrl' ); |
106 | |
107 | if ( !$url ) { |
108 | // no remote url, fall back to local wikitext |
109 | return $wikitext; |
110 | } |
111 | |
112 | $url = wfAppendQuery( |
113 | $url, |
114 | [ |
115 | 'action' => 'expandtemplates', |
116 | 'title' => 'Special:BlankPage/GlobalBlockReasonFormatter', |
117 | 'text' => $wikitext, |
118 | 'uselang' => $langCode, |
119 | 'prop' => 'wikitext', |
120 | 'formatversion' => 2, |
121 | 'format' => 'json', |
122 | ] |
123 | ); |
124 | |
125 | $req = $this->httpRequestFactory->create( |
126 | $url, |
127 | [ |
128 | 'userAgent' => $this->httpRequestFactory->getUserAgent() . ' GlobalBlockReasonFormatter' |
129 | ], |
130 | __METHOD__ |
131 | ); |
132 | |
133 | $status = $req->execute(); |
134 | |
135 | [ $errorStatus, $warningStatus ] = $status->splitByErrorType(); |
136 | if ( !$warningStatus->isGood() ) { |
137 | $this->logger->warning( |
138 | $warningStatus->getWikiText( false, false, 'en' ), |
139 | [ 'exception' => new RuntimeException() ] |
140 | ); |
141 | } |
142 | |
143 | if ( $errorStatus->isGood() ) { |
144 | $json = $req->getContent(); |
145 | $decoded = FormatJson::decode( $json, true ); |
146 | |
147 | if ( |
148 | is_array( $decoded ) |
149 | && array_key_exists( 'expandtemplates', $decoded ) |
150 | && array_key_exists( 'wikitext', $decoded['expandtemplates'] ) |
151 | ) { |
152 | return $decoded['expandtemplates']['wikitext']; |
153 | } |
154 | |
155 | $this->logger->warning( |
156 | 'Got API response with unexpected formatting while parsing global block reason "{blockReason}"', |
157 | [ 'response' => $decoded, 'blockReason' => $wikitext ] |
158 | ); |
159 | } else { |
160 | $this->logger->error( |
161 | $warningStatus->getWikiText( false, false, 'en' ), |
162 | [ 'exception' => new RuntimeException() ] |
163 | ); |
164 | } |
165 | |
166 | // fallback to param |
167 | return $wikitext; |
168 | } |
169 | } |