Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
79.10% |
106 / 134 |
|
76.92% |
10 / 13 |
CRAP | |
0.00% |
0 / 1 |
PreliminaryCheckPager | |
79.10% |
106 / 134 |
|
76.92% |
10 / 13 |
51.17 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 | |||
getTableClass | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getCellAttrs | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
3 | |||
formatValue | |
64.18% |
43 / 67 |
|
0.00% |
0 / 1 |
30.28 | |||
getIndexField | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getFieldNames | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
4 | |||
getQueryInfo | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
preprocessResults | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
isGlobalCheck | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getCentralReplicaDB | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
isFieldSortable | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDefaultSort | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPagingQueries | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 |
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 | * @ingroup Pager |
20 | */ |
21 | |
22 | namespace MediaWiki\CheckUser\Investigate\Pagers; |
23 | |
24 | use ExtensionRegistry; |
25 | use IContextSource; |
26 | use MediaWiki\CheckUser\Investigate\Services\PreliminaryCheckService; |
27 | use MediaWiki\CheckUser\Services\TokenQueryManager; |
28 | use MediaWiki\Extension\CentralAuth\CentralAuthDatabaseManager; |
29 | use MediaWiki\Extension\CentralAuth\CentralAuthServices; |
30 | use MediaWiki\Html\Html; |
31 | use MediaWiki\Linker\LinkRenderer; |
32 | use MediaWiki\Pager\TablePager; |
33 | use MediaWiki\SpecialPage\SpecialPage; |
34 | use MediaWiki\Title\NamespaceInfo; |
35 | use MediaWiki\User\User; |
36 | use MediaWiki\User\UserFactory; |
37 | use MediaWiki\WikiMap\WikiMap; |
38 | use Wikimedia\Rdbms\FakeResultWrapper; |
39 | use Wikimedia\Rdbms\IReadableDatabase; |
40 | |
41 | /** |
42 | * @ingroup Pager |
43 | */ |
44 | class PreliminaryCheckPager extends TablePager { |
45 | private NamespaceInfo $namespaceInfo; |
46 | private ExtensionRegistry $extensionRegistry; |
47 | private TokenQueryManager $tokenQueryManager; |
48 | private PreliminaryCheckService $preliminaryCheckService; |
49 | private UserFactory $userFactory; |
50 | |
51 | /** @var array Data loaded from the token provided in the request. */ |
52 | protected $tokenData; |
53 | |
54 | /** @var string[] Array of column name to translated table header message */ |
55 | private $fieldNames; |
56 | |
57 | /** |
58 | * @param IContextSource $context |
59 | * @param LinkRenderer $linkRenderer |
60 | * @param NamespaceInfo $namespaceInfo |
61 | * @param TokenQueryManager $tokenQueryManager |
62 | * @param ExtensionRegistry $extensionRegistry |
63 | * @param PreliminaryCheckService $preliminaryCheckService |
64 | * @param UserFactory $userFactory |
65 | */ |
66 | public function __construct( |
67 | IContextSource $context, |
68 | LinkRenderer $linkRenderer, |
69 | NamespaceInfo $namespaceInfo, |
70 | TokenQueryManager $tokenQueryManager, |
71 | ExtensionRegistry $extensionRegistry, |
72 | PreliminaryCheckService $preliminaryCheckService, |
73 | UserFactory $userFactory |
74 | ) { |
75 | // This must be done before getIndexField is called by the TablePager constructor |
76 | $this->extensionRegistry = $extensionRegistry; |
77 | if ( $this->isGlobalCheck() ) { |
78 | // @phan-suppress-next-line PhanPossiblyNullTypeMismatchProperty |
79 | $this->mDb = $this->getCentralReplicaDB(); |
80 | } |
81 | |
82 | parent::__construct( $context, $linkRenderer ); |
83 | $this->namespaceInfo = $namespaceInfo; |
84 | $this->preliminaryCheckService = $preliminaryCheckService; |
85 | $this->tokenQueryManager = $tokenQueryManager; |
86 | $this->userFactory = $userFactory; |
87 | |
88 | $this->tokenData = $tokenQueryManager->getDataFromRequest( $context->getRequest() ); |
89 | $this->mOffset = $this->tokenData['offset'] ?? ''; |
90 | } |
91 | |
92 | /** |
93 | * @inheritDoc |
94 | */ |
95 | protected function getTableClass() { |
96 | return parent::getTableClass() . |
97 | ' ext-checkuser-investigate-table' . |
98 | ' ext-checkuser-investigate-table-preliminary-check'; |
99 | } |
100 | |
101 | /** |
102 | * @inheritDoc |
103 | */ |
104 | public function getCellAttrs( $field, $value ) { |
105 | $attributes = parent::getCellAttrs( $field, $value ); |
106 | $attributes['class'] ??= ''; |
107 | |
108 | switch ( $field ) { |
109 | case 'wiki': |
110 | $attributes['class'] .= ' ext-checkuser-investigate-table-cell-interactive'; |
111 | $attributes['class'] .= ' ext-checkuser-investigate-table-cell-pinnable'; |
112 | $attributes['data-field'] = $field; |
113 | $attributes['data-value'] = $value; |
114 | break; |
115 | case 'registration': |
116 | $attributes['class'] .= ' ext-checkuser-investigate-table-cell-interactive'; |
117 | $attributes['class'] .= ' ext-checkuser-investigate-table-cell-pinnable'; |
118 | $date = $this->getLanguage()->userDate( |
119 | $value, |
120 | $this->getUser(), |
121 | [ 'format' => 'ISO 8601' ] |
122 | ); |
123 | $attributes['data-field'] = $field; |
124 | $attributes['data-value'] = $date; |
125 | break; |
126 | } |
127 | |
128 | // Add each cell to the tab index. |
129 | $attributes['tabindex'] = 0; |
130 | |
131 | return $attributes; |
132 | } |
133 | |
134 | /** |
135 | * @param string $name |
136 | * @param mixed $value preprocessed by {@link PreliminaryCheckService::getAdditionalLocalData} |
137 | * @return string |
138 | */ |
139 | public function formatValue( $name, $value ) { |
140 | $language = $this->getLanguage(); |
141 | $row = $this->mCurrentRow; |
142 | |
143 | $user = $this->userFactory->newFromName( $row->name ); |
144 | $userIsHidden = $user !== null && $user->isHidden() && !$this->getAuthority()->isAllowed( 'hideuser' ); |
145 | |
146 | $formatted = ''; |
147 | switch ( $name ) { |
148 | case 'name': |
149 | // Hide the username if it is hidden from the current authority. |
150 | if ( $userIsHidden ) { |
151 | $formatted = $this->msg( 'rev-deleted-user' )->text(); |
152 | } else { |
153 | $formatted = htmlspecialchars( $value ); |
154 | } |
155 | break; |
156 | case 'registration': |
157 | if ( !$userIsHidden ) { |
158 | $formatted = htmlspecialchars( |
159 | $language->userTimeAndDate( $value, $this->getUser() ) |
160 | ); |
161 | } |
162 | break; |
163 | case 'wiki': |
164 | $wiki = WikiMap::getWiki( $row->wiki ); |
165 | if ( $wiki ) { |
166 | $formatted = Html::element( |
167 | 'a', |
168 | [ |
169 | 'href' => $wiki->getFullUrl( |
170 | $this->namespaceInfo->getCanonicalName( NS_USER ) . ':' . $row->name |
171 | ), |
172 | ], |
173 | $wiki->getDisplayName() |
174 | ); |
175 | } else { |
176 | $formatted = $this->msg( 'checkuser-investigate-preliminary-table-cell-wiki-nowiki' )->text(); |
177 | } |
178 | break; |
179 | case 'editcount': |
180 | if ( $userIsHidden ) { |
181 | return ''; |
182 | } |
183 | $wiki = WikiMap::getWiki( $row->wiki ); |
184 | if ( $wiki ) { |
185 | $formatted = Html::rawElement( |
186 | 'a', |
187 | [ |
188 | 'href' => $wiki->getFullUrl( |
189 | $this->namespaceInfo->getCanonicalName( NS_SPECIAL ) . ':Contributions/' . $row->name |
190 | ), |
191 | ], |
192 | $this->msg( |
193 | 'checkuser-investigate-preliminary-table-cell-edits', |
194 | $value |
195 | )->parse() |
196 | ); |
197 | } else { |
198 | $formatted = $this->getLinkRenderer()->makeKnownLink( |
199 | SpecialPage::getTitleFor( 'Contributions', $row->name ), |
200 | $this->msg( |
201 | 'checkuser-investigate-preliminary-table-cell-edits', |
202 | $value |
203 | )->text() |
204 | ); |
205 | } |
206 | break; |
207 | case 'blocked': |
208 | if ( !$userIsHidden ) { |
209 | $formatted = $this->msg( $value ? |
210 | 'checkuser-investigate-preliminary-table-cell-blocked' : |
211 | 'checkuser-investigate-preliminary-table-cell-unblocked' |
212 | )->parse(); |
213 | } |
214 | break; |
215 | case 'groups': |
216 | if ( !$userIsHidden ) { |
217 | $formatted = htmlspecialchars( implode( ', ', $value ) ); |
218 | } |
219 | break; |
220 | } |
221 | |
222 | return $formatted; |
223 | } |
224 | |
225 | /** |
226 | * @inheritDoc |
227 | */ |
228 | public function getIndexField() { |
229 | return $this->isGlobalCheck() ? [ [ 'lu_name', 'lu_wiki' ] ] : 'user_name'; |
230 | } |
231 | |
232 | /** |
233 | * @inheritDoc |
234 | */ |
235 | public function getFieldNames() { |
236 | if ( $this->fieldNames === null ) { |
237 | $this->fieldNames = [ |
238 | 'name' => 'checkuser-investigate-preliminary-table-header-name', |
239 | 'registration' => 'checkuser-investigate-preliminary-table-header-registration', |
240 | 'wiki' => 'checkuser-investigate-preliminary-table-header-wiki', |
241 | 'editcount' => 'checkuser-investigate-preliminary-table-header-editcount', |
242 | 'blocked' => 'checkuser-investigate-preliminary-table-header-blocked', |
243 | 'groups' => 'checkuser-investigate-preliminary-table-header-groups', |
244 | ]; |
245 | |
246 | if ( !$this->isGlobalCheck() ) { |
247 | unset( $this->fieldNames['wiki'] ); |
248 | } |
249 | |
250 | foreach ( $this->fieldNames as &$val ) { |
251 | $val = $this->msg( $val )->text(); |
252 | } |
253 | } |
254 | return $this->fieldNames; |
255 | } |
256 | |
257 | /** |
258 | * @inheritDoc |
259 | */ |
260 | public function getQueryInfo() { |
261 | $targets = $this->tokenData['targets'] ?? []; |
262 | $users = array_filter( array_map( [ User::class, 'newFromName' ], $targets ), static function ( $user ) { |
263 | return (bool)$user; |
264 | } ); |
265 | |
266 | return $this->preliminaryCheckService->getQueryInfo( $users ); |
267 | } |
268 | |
269 | /** |
270 | * @inheritDoc |
271 | */ |
272 | public function preprocessResults( $result ) { |
273 | $this->mResult = new FakeResultWrapper( |
274 | $this->preliminaryCheckService->preprocessResults( $result ) |
275 | ); |
276 | } |
277 | |
278 | /** |
279 | * @return bool |
280 | */ |
281 | public function isGlobalCheck(): bool { |
282 | return $this->extensionRegistry->isLoaded( 'CentralAuth' ) |
283 | && class_exists( CentralAuthDatabaseManager::class ); |
284 | } |
285 | |
286 | /** |
287 | * @return IReadableDatabase|null |
288 | */ |
289 | protected function getCentralReplicaDB(): ?IReadableDatabase { |
290 | if ( class_exists( CentralAuthDatabaseManager::class ) ) { |
291 | return CentralAuthServices::getDatabaseManager()->getCentralReplicaDB(); |
292 | } |
293 | return null; |
294 | } |
295 | |
296 | /** |
297 | * @inheritDoc |
298 | */ |
299 | public function isFieldSortable( $field ) { |
300 | return false; |
301 | } |
302 | |
303 | /** |
304 | * @inheritDoc |
305 | */ |
306 | public function getDefaultSort() { |
307 | return ''; |
308 | } |
309 | |
310 | /** |
311 | * @inheritDoc |
312 | * |
313 | * Conceal the offset which may reveal private data. |
314 | */ |
315 | public function getPagingQueries() { |
316 | return $this->tokenQueryManager->getPagingQueries( |
317 | $this->getRequest(), parent::getPagingQueries() |
318 | ); |
319 | } |
320 | } |