Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
5.17% |
9 / 174 |
|
14.29% |
1 / 7 |
CRAP | |
0.00% |
0 / 1 |
AbuseFilterViewExamine | |
5.17% |
9 / 174 |
|
14.29% |
1 / 7 |
696.53 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
show | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
56 | |||
showSearch | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
2 | |||
showResults | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
30 | |||
showExaminerForRC | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
20 | |||
showExaminerForLogEntry | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
56 | |||
showExaminer | |
0.00% |
0 / 40 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\AbuseFilter\View; |
4 | |
5 | use ChangesList; |
6 | use HTMLForm; |
7 | use IContextSource; |
8 | use LogicException; |
9 | use MediaWiki\Extension\AbuseFilter\AbuseFilterChangesList; |
10 | use MediaWiki\Extension\AbuseFilter\AbuseFilterPermissionManager; |
11 | use MediaWiki\Extension\AbuseFilter\CentralDBNotAvailableException; |
12 | use MediaWiki\Extension\AbuseFilter\EditBox\EditBoxBuilderFactory; |
13 | use MediaWiki\Extension\AbuseFilter\FilterLookup; |
14 | use MediaWiki\Extension\AbuseFilter\Pager\AbuseFilterExaminePager; |
15 | use MediaWiki\Extension\AbuseFilter\Special\SpecialAbuseLog; |
16 | use MediaWiki\Extension\AbuseFilter\VariableGenerator\VariableGeneratorFactory; |
17 | use MediaWiki\Extension\AbuseFilter\Variables\VariableHolder; |
18 | use MediaWiki\Extension\AbuseFilter\Variables\VariablesBlobStore; |
19 | use MediaWiki\Extension\AbuseFilter\Variables\VariablesFormatter; |
20 | use MediaWiki\Extension\AbuseFilter\Variables\VariablesManager; |
21 | use MediaWiki\Linker\LinkRenderer; |
22 | use MediaWiki\Revision\RevisionRecord; |
23 | use MediaWiki\Title\Title; |
24 | use OOUI; |
25 | use RecentChange; |
26 | use Wikimedia\Rdbms\LBFactory; |
27 | use Xml; |
28 | |
29 | class AbuseFilterViewExamine extends AbuseFilterView { |
30 | /** |
31 | * @var string The rules of the filter we're examining |
32 | */ |
33 | private $testFilter; |
34 | /** |
35 | * @var LBFactory |
36 | */ |
37 | private $lbFactory; |
38 | /** |
39 | * @var FilterLookup |
40 | */ |
41 | private $filterLookup; |
42 | /** |
43 | * @var EditBoxBuilderFactory |
44 | */ |
45 | private $boxBuilderFactory; |
46 | /** |
47 | * @var VariablesBlobStore |
48 | */ |
49 | private $varBlobStore; |
50 | /** |
51 | * @var VariablesFormatter |
52 | */ |
53 | private $variablesFormatter; |
54 | /** |
55 | * @var VariablesManager |
56 | */ |
57 | private $varManager; |
58 | /** |
59 | * @var VariableGeneratorFactory |
60 | */ |
61 | private $varGeneratorFactory; |
62 | |
63 | /** |
64 | * @param LBFactory $lbFactory |
65 | * @param AbuseFilterPermissionManager $afPermManager |
66 | * @param FilterLookup $filterLookup |
67 | * @param EditBoxBuilderFactory $boxBuilderFactory |
68 | * @param VariablesBlobStore $varBlobStore |
69 | * @param VariablesFormatter $variablesFormatter |
70 | * @param VariablesManager $varManager |
71 | * @param VariableGeneratorFactory $varGeneratorFactory |
72 | * @param IContextSource $context |
73 | * @param LinkRenderer $linkRenderer |
74 | * @param string $basePageName |
75 | * @param array $params |
76 | */ |
77 | public function __construct( |
78 | LBFactory $lbFactory, |
79 | AbuseFilterPermissionManager $afPermManager, |
80 | FilterLookup $filterLookup, |
81 | EditBoxBuilderFactory $boxBuilderFactory, |
82 | VariablesBlobStore $varBlobStore, |
83 | VariablesFormatter $variablesFormatter, |
84 | VariablesManager $varManager, |
85 | VariableGeneratorFactory $varGeneratorFactory, |
86 | IContextSource $context, |
87 | LinkRenderer $linkRenderer, |
88 | string $basePageName, |
89 | array $params |
90 | ) { |
91 | parent::__construct( $afPermManager, $context, $linkRenderer, $basePageName, $params ); |
92 | $this->lbFactory = $lbFactory; |
93 | $this->filterLookup = $filterLookup; |
94 | $this->boxBuilderFactory = $boxBuilderFactory; |
95 | $this->varBlobStore = $varBlobStore; |
96 | $this->variablesFormatter = $variablesFormatter; |
97 | $this->variablesFormatter->setMessageLocalizer( $context ); |
98 | $this->varManager = $varManager; |
99 | $this->varGeneratorFactory = $varGeneratorFactory; |
100 | } |
101 | |
102 | /** |
103 | * Shows the page |
104 | */ |
105 | public function show() { |
106 | $out = $this->getOutput(); |
107 | $out->setPageTitleMsg( $this->msg( 'abusefilter-examine' ) ); |
108 | $out->addHelpLink( 'Extension:AbuseFilter/Rules format' ); |
109 | if ( $this->afPermManager->canUseTestTools( $this->getAuthority() ) ) { |
110 | $out->addWikiMsg( 'abusefilter-examine-intro' ); |
111 | } else { |
112 | $out->addWikiMsg( 'abusefilter-examine-intro-examine-only' ); |
113 | } |
114 | |
115 | $this->testFilter = $this->getRequest()->getText( 'testfilter' ); |
116 | |
117 | // Check if we've got a subpage |
118 | if ( count( $this->mParams ) > 1 && is_numeric( $this->mParams[1] ) ) { |
119 | $this->showExaminerForRC( $this->mParams[1] ); |
120 | } elseif ( count( $this->mParams ) > 2 |
121 | && $this->mParams[1] === 'log' |
122 | && is_numeric( $this->mParams[2] ) |
123 | ) { |
124 | $this->showExaminerForLogEntry( $this->mParams[2] ); |
125 | } else { |
126 | $this->showSearch(); |
127 | } |
128 | } |
129 | |
130 | /** |
131 | * Shows the search form |
132 | */ |
133 | public function showSearch() { |
134 | $RCMaxAge = $this->getConfig()->get( 'RCMaxAge' ); |
135 | $min = wfTimestamp( TS_ISO_8601, time() - $RCMaxAge ); |
136 | $max = wfTimestampNow(); |
137 | $formDescriptor = [ |
138 | 'SearchUser' => [ |
139 | 'label-message' => 'abusefilter-test-user', |
140 | 'type' => 'user', |
141 | 'ipallowed' => true, |
142 | ], |
143 | 'SearchPeriodStart' => [ |
144 | 'label-message' => 'abusefilter-test-period-start', |
145 | 'type' => 'datetime', |
146 | 'min' => $min, |
147 | 'max' => $max, |
148 | ], |
149 | 'SearchPeriodEnd' => [ |
150 | 'label-message' => 'abusefilter-test-period-end', |
151 | 'type' => 'datetime', |
152 | 'min' => $min, |
153 | 'max' => $max, |
154 | ], |
155 | ]; |
156 | HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ) |
157 | ->addHiddenField( 'testfilter', $this->testFilter ) |
158 | ->setWrapperLegendMsg( 'abusefilter-examine-legend' ) |
159 | ->setSubmitTextMsg( 'abusefilter-examine-submit' ) |
160 | ->setSubmitCallback( [ $this, 'showResults' ] ) |
161 | ->showAlways(); |
162 | } |
163 | |
164 | /** |
165 | * Show search results, called as submit callback by HTMLForm |
166 | * @param array $formData |
167 | * @param HTMLForm $form |
168 | * @return bool |
169 | */ |
170 | public function showResults( array $formData, HTMLForm $form ): bool { |
171 | $changesList = new AbuseFilterChangesList( $this->getContext(), $this->testFilter ); |
172 | |
173 | $dbr = $this->lbFactory->getReplicaDatabase(); |
174 | $conds = $this->buildVisibilityConditions( $dbr, $this->getAuthority() ); |
175 | $conds[] = $this->buildTestConditions( $dbr ); |
176 | |
177 | // Normalise username |
178 | $userTitle = Title::newFromText( $formData['SearchUser'], NS_USER ); |
179 | $userName = $userTitle ? $userTitle->getText() : ''; |
180 | |
181 | if ( $userName !== '' ) { |
182 | $rcQuery = RecentChange::getQueryInfo(); |
183 | $conds[$rcQuery['fields']['rc_user_text']] = $userName; |
184 | } |
185 | |
186 | $startTS = strtotime( $formData['SearchPeriodStart'] ); |
187 | if ( $startTS ) { |
188 | $conds[] = 'rc_timestamp>=' . $dbr->addQuotes( $dbr->timestamp( $startTS ) ); |
189 | } |
190 | $endTS = strtotime( $formData['SearchPeriodEnd'] ); |
191 | if ( $endTS ) { |
192 | $conds[] = 'rc_timestamp<=' . $dbr->addQuotes( $dbr->timestamp( $endTS ) ); |
193 | } |
194 | $pager = new AbuseFilterExaminePager( |
195 | $changesList, |
196 | $this->linkRenderer, |
197 | $dbr, |
198 | $this->getTitle( 'examine' ), |
199 | $conds |
200 | ); |
201 | |
202 | $output = $changesList->beginRecentChangesList() |
203 | . $pager->getNavigationBar() |
204 | . $pager->getBody() |
205 | . $pager->getNavigationBar() |
206 | . $changesList->endRecentChangesList(); |
207 | |
208 | $form->addPostHtml( $output ); |
209 | return true; |
210 | } |
211 | |
212 | /** |
213 | * @param int $rcid |
214 | */ |
215 | public function showExaminerForRC( $rcid ) { |
216 | // Get data |
217 | $rc = RecentChange::newFromId( $rcid ); |
218 | $out = $this->getOutput(); |
219 | if ( !$rc ) { |
220 | $out->addWikiMsg( 'abusefilter-examine-notfound' ); |
221 | return; |
222 | } |
223 | |
224 | if ( !ChangesList::userCan( $rc, RevisionRecord::SUPPRESSED_ALL ) ) { |
225 | $out->addWikiMsg( 'abusefilter-log-details-hidden-implicit' ); |
226 | return; |
227 | } |
228 | |
229 | $varGenerator = $this->varGeneratorFactory->newRCGenerator( $rc, $this->getUser() ); |
230 | $vars = $varGenerator->getVars() ?: new VariableHolder(); |
231 | $out->addJsConfigVars( [ |
232 | 'wgAbuseFilterVariables' => $this->varManager->dumpAllVars( $vars, true ), |
233 | 'abuseFilterExamine' => [ 'type' => 'rc', 'id' => $rcid ] |
234 | ] ); |
235 | |
236 | $this->showExaminer( $vars ); |
237 | } |
238 | |
239 | /** |
240 | * @param int $logid |
241 | */ |
242 | public function showExaminerForLogEntry( $logid ) { |
243 | // Get data |
244 | $dbr = $this->lbFactory->getReplicaDatabase(); |
245 | $performer = $this->getAuthority(); |
246 | $out = $this->getOutput(); |
247 | |
248 | $row = $dbr->selectRow( |
249 | 'abuse_filter_log', |
250 | [ |
251 | 'afl_deleted', |
252 | 'afl_var_dump', |
253 | 'afl_rev_id', |
254 | 'afl_filter_id', |
255 | 'afl_global' |
256 | ], |
257 | [ 'afl_id' => $logid ], |
258 | __METHOD__ |
259 | ); |
260 | |
261 | if ( !$row ) { |
262 | $out->addWikiMsg( 'abusefilter-examine-notfound' ); |
263 | return; |
264 | } |
265 | |
266 | try { |
267 | $isHidden = $this->filterLookup->getFilter( $row->afl_filter_id, $row->afl_global )->isHidden(); |
268 | } catch ( CentralDBNotAvailableException $_ ) { |
269 | // Conservatively assume that it's hidden, like in SpecialAbuseLog |
270 | $isHidden = true; |
271 | } |
272 | if ( !$this->afPermManager->canSeeLogDetailsForFilter( $performer, $isHidden ) ) { |
273 | $out->addWikiMsg( 'abusefilter-log-cannot-see-details' ); |
274 | return; |
275 | } |
276 | |
277 | $visibility = SpecialAbuseLog::getEntryVisibilityForUser( $row, $performer, $this->afPermManager ); |
278 | if ( $visibility !== SpecialAbuseLog::VISIBILITY_VISIBLE ) { |
279 | if ( $visibility === SpecialAbuseLog::VISIBILITY_HIDDEN ) { |
280 | $msg = 'abusefilter-log-details-hidden'; |
281 | } elseif ( $visibility === SpecialAbuseLog::VISIBILITY_HIDDEN_IMPLICIT ) { |
282 | $msg = 'abusefilter-log-details-hidden-implicit'; |
283 | } else { |
284 | throw new LogicException( "Unexpected visibility $visibility" ); |
285 | } |
286 | $out->addWikiMsg( $msg ); |
287 | return; |
288 | } |
289 | |
290 | $vars = $this->varBlobStore->loadVarDump( $row->afl_var_dump ); |
291 | $out->addJsConfigVars( [ |
292 | 'wgAbuseFilterVariables' => $this->varManager->dumpAllVars( $vars, true ), |
293 | 'abuseFilterExamine' => [ 'type' => 'log', 'id' => $logid ] |
294 | ] ); |
295 | $this->showExaminer( $vars ); |
296 | } |
297 | |
298 | /** |
299 | * @param VariableHolder|null $vars |
300 | */ |
301 | public function showExaminer( ?VariableHolder $vars ) { |
302 | $output = $this->getOutput(); |
303 | $output->enableOOUI(); |
304 | |
305 | if ( !$vars ) { |
306 | $output->addWikiMsg( 'abusefilter-examine-incompatible' ); |
307 | return; |
308 | } |
309 | |
310 | $html = ''; |
311 | |
312 | $output->addModules( 'ext.abuseFilter.examine' ); |
313 | |
314 | // Add test bit |
315 | if ( $this->afPermManager->canUseTestTools( $this->getAuthority() ) ) { |
316 | $boxBuilder = $this->boxBuilderFactory->newEditBoxBuilder( |
317 | $this, |
318 | $this->getAuthority(), |
319 | $output |
320 | ); |
321 | |
322 | $tester = Xml::tags( 'h2', null, $this->msg( 'abusefilter-examine-test' )->parse() ); |
323 | $tester .= $boxBuilder->buildEditBox( $this->testFilter, false, false, false ); |
324 | $tester .= $this->buildFilterLoader(); |
325 | $html .= Xml::tags( 'div', [ 'id' => 'mw-abusefilter-examine-editor' ], $tester ); |
326 | $html .= Xml::tags( 'p', |
327 | null, |
328 | new OOUI\ButtonInputWidget( |
329 | [ |
330 | 'label' => $this->msg( 'abusefilter-examine-test-button' )->text(), |
331 | 'id' => 'mw-abusefilter-examine-test', |
332 | 'flags' => [ 'primary', 'progressive' ] |
333 | ] |
334 | ) . |
335 | Xml::element( 'div', |
336 | [ |
337 | 'id' => 'mw-abusefilter-syntaxresult', |
338 | 'style' => 'display: none;' |
339 | ], ' ' |
340 | ) |
341 | ); |
342 | } |
343 | |
344 | // Variable dump |
345 | $html .= Xml::tags( |
346 | 'h2', |
347 | null, |
348 | $this->msg( 'abusefilter-examine-vars' )->parse() |
349 | ); |
350 | $html .= $this->variablesFormatter->buildVarDumpTable( $vars ); |
351 | |
352 | $output->addHTML( $html ); |
353 | } |
354 | |
355 | } |