Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 155 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
SpecialDeletedContributions | |
0.00% |
0 / 154 |
|
0.00% |
0 / 6 |
650 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 67 |
|
0.00% |
0 / 1 |
182 | |||
getSubTitle | |
0.00% |
0 / 46 |
|
0.00% |
0 / 1 |
56 | |||
getForm | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
2 | |||
prefixSearchSubpages | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Implements Special:DeletedContributions |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | * @ingroup SpecialPage |
22 | */ |
23 | |
24 | namespace MediaWiki\Specials; |
25 | |
26 | use LogEventsList; |
27 | use MediaWiki\Block\Block; |
28 | use MediaWiki\Block\DatabaseBlockStore; |
29 | use MediaWiki\Cache\LinkBatchFactory; |
30 | use MediaWiki\CommentFormatter\CommentFormatter; |
31 | use MediaWiki\Html\FormOptions; |
32 | use MediaWiki\HTMLForm\HTMLForm; |
33 | use MediaWiki\MainConfigNames; |
34 | use MediaWiki\Pager\DeletedContribsPager; |
35 | use MediaWiki\Permissions\PermissionManager; |
36 | use MediaWiki\Revision\RevisionFactory; |
37 | use MediaWiki\SpecialPage\SpecialPage; |
38 | use MediaWiki\Title\NamespaceInfo; |
39 | use MediaWiki\User\User; |
40 | use MediaWiki\User\UserFactory; |
41 | use MediaWiki\User\UserNamePrefixSearch; |
42 | use MediaWiki\User\UserNameUtils; |
43 | use MediaWiki\User\UserRigorOptions; |
44 | use Wikimedia\IPUtils; |
45 | use Wikimedia\Rdbms\IConnectionProvider; |
46 | |
47 | /** |
48 | * Implements Special:DeletedContributions to display archived revisions |
49 | * @ingroup SpecialPage |
50 | */ |
51 | class SpecialDeletedContributions extends SpecialPage { |
52 | /** @var FormOptions */ |
53 | protected $mOpts; |
54 | |
55 | private PermissionManager $permissionManager; |
56 | private IConnectionProvider $dbProvider; |
57 | private RevisionFactory $revisionFactory; |
58 | private NamespaceInfo $namespaceInfo; |
59 | private UserFactory $userFactory; |
60 | private UserNameUtils $userNameUtils; |
61 | private UserNamePrefixSearch $userNamePrefixSearch; |
62 | private CommentFormatter $commentFormatter; |
63 | private LinkBatchFactory $linkBatchFactory; |
64 | private DatabaseBlockStore $blockStore; |
65 | |
66 | /** |
67 | * @param PermissionManager $permissionManager |
68 | * @param IConnectionProvider $dbProvider |
69 | * @param RevisionFactory $revisionFactory |
70 | * @param NamespaceInfo $namespaceInfo |
71 | * @param UserFactory $userFactory |
72 | * @param UserNameUtils $userNameUtils |
73 | * @param UserNamePrefixSearch $userNamePrefixSearch |
74 | * @param CommentFormatter $commentFormatter |
75 | * @param LinkBatchFactory $linkBatchFactory |
76 | * @param DatabaseBlockStore $blockStore |
77 | */ |
78 | public function __construct( |
79 | PermissionManager $permissionManager, |
80 | IConnectionProvider $dbProvider, |
81 | RevisionFactory $revisionFactory, |
82 | NamespaceInfo $namespaceInfo, |
83 | UserFactory $userFactory, |
84 | UserNameUtils $userNameUtils, |
85 | UserNamePrefixSearch $userNamePrefixSearch, |
86 | CommentFormatter $commentFormatter, |
87 | LinkBatchFactory $linkBatchFactory, |
88 | DatabaseBlockStore $blockStore |
89 | ) { |
90 | parent::__construct( 'DeletedContributions', 'deletedhistory' ); |
91 | $this->permissionManager = $permissionManager; |
92 | $this->dbProvider = $dbProvider; |
93 | $this->revisionFactory = $revisionFactory; |
94 | $this->namespaceInfo = $namespaceInfo; |
95 | $this->userFactory = $userFactory; |
96 | $this->userNameUtils = $userNameUtils; |
97 | $this->userNamePrefixSearch = $userNamePrefixSearch; |
98 | $this->commentFormatter = $commentFormatter; |
99 | $this->linkBatchFactory = $linkBatchFactory; |
100 | $this->blockStore = $blockStore; |
101 | } |
102 | |
103 | /** |
104 | * Special page "deleted user contributions". |
105 | * Shows a list of the deleted contributions of a user. |
106 | * |
107 | * @param string|null $par user name of the user for which to show the contributions |
108 | */ |
109 | public function execute( $par ) { |
110 | $this->setHeaders(); |
111 | $this->outputHeader(); |
112 | $this->checkPermissions(); |
113 | $out = $this->getOutput(); |
114 | $out->addModuleStyles( [ |
115 | 'mediawiki.interface.helpers.styles', |
116 | 'mediawiki.special.changeslist', |
117 | ] ); |
118 | $this->addHelpLink( 'Help:User contributions' ); |
119 | |
120 | $opts = new FormOptions(); |
121 | |
122 | $opts->add( 'target', '' ); |
123 | $opts->add( 'namespace', '' ); |
124 | $opts->add( 'limit', 20 ); |
125 | |
126 | $opts->fetchValuesFromRequest( $this->getRequest() ); |
127 | $opts->validateIntBounds( 'limit', 0, |
128 | $this->getConfig()->get( MainConfigNames::QueryPageDefaultLimit ) ); |
129 | |
130 | if ( $par !== null ) { |
131 | // Beautify the username |
132 | $par = $this->userNameUtils->getCanonical( $par, UserRigorOptions::RIGOR_NONE ); |
133 | $opts->setValue( 'target', (string)$par ); |
134 | } |
135 | |
136 | $ns = $opts->getValue( 'namespace' ); |
137 | if ( $ns !== null && $ns !== '' ) { |
138 | $opts->setValue( 'namespace', intval( $ns ) ); |
139 | } |
140 | |
141 | $this->mOpts = $opts; |
142 | |
143 | $target = trim( $opts->getValue( 'target' ) ); |
144 | if ( !strlen( $target ) ) { |
145 | $this->getForm(); |
146 | |
147 | return; |
148 | } |
149 | |
150 | $userObj = $this->userFactory->newFromName( $target, UserRigorOptions::RIGOR_NONE ); |
151 | if ( !$userObj ) { |
152 | $this->getForm(); |
153 | |
154 | return; |
155 | } |
156 | // Only set valid local user as the relevant user (T344886) |
157 | // Uses the same condition as the SpecialContributions class did |
158 | if ( !IPUtils::isValidRange( $target ) && |
159 | ( $this->userNameUtils->isIP( $target ) || $userObj->isRegistered() ) |
160 | ) { |
161 | $this->getSkin()->setRelevantUser( $userObj ); |
162 | } |
163 | |
164 | $target = $userObj->getName(); |
165 | |
166 | $out->addSubtitle( $this->getSubTitle( $userObj ) ); |
167 | $out->setPageTitleMsg( $this->msg( 'deletedcontributions-title' )->plaintextParams( $target ) ); |
168 | |
169 | $this->getForm(); |
170 | |
171 | $pager = new DeletedContribsPager( |
172 | $this->getContext(), |
173 | $this->getHookContainer(), |
174 | $this->getLinkRenderer(), |
175 | $this->dbProvider, |
176 | $this->revisionFactory, |
177 | $this->commentFormatter, |
178 | $this->linkBatchFactory, |
179 | $target, |
180 | $opts->getValue( 'namespace' ) |
181 | ); |
182 | if ( !$pager->getNumRows() ) { |
183 | $out->addWikiMsg( 'nocontribs' ); |
184 | |
185 | return; |
186 | } |
187 | |
188 | # Show a message about replica DB lag, if applicable |
189 | $lag = $pager->getDatabase()->getSessionLagStatus()['lag']; |
190 | if ( $lag > 0 ) { |
191 | $out->showLagWarning( $lag ); |
192 | } |
193 | |
194 | $out->addHTML( |
195 | '<p>' . $pager->getNavigationBar() . '</p>' . |
196 | $pager->getBody() . |
197 | '<p>' . $pager->getNavigationBar() . '</p>' ); |
198 | |
199 | # If there were contributions, and it was a valid user or IP, show |
200 | # the appropriate "footer" message - WHOIS tools, etc. |
201 | $message = IPUtils::isIPAddress( $target ) ? |
202 | 'sp-contributions-footer-anon' : |
203 | 'sp-contributions-footer'; |
204 | |
205 | if ( !$this->msg( $message )->isDisabled() ) { |
206 | $out->wrapWikiMsg( |
207 | "<div class='mw-contributions-footer'>\n$1\n</div>", |
208 | [ $message, $target ] |
209 | ); |
210 | } |
211 | } |
212 | |
213 | /** |
214 | * Generates the subheading with links |
215 | * @param User $userObj User object for the target |
216 | * @return string Appropriately-escaped HTML to be output literally |
217 | */ |
218 | private function getSubTitle( $userObj ) { |
219 | $linkRenderer = $this->getLinkRenderer(); |
220 | if ( $userObj->isAnon() ) { |
221 | $user = htmlspecialchars( $userObj->getName() ); |
222 | } else { |
223 | $user = $linkRenderer->makeLink( $userObj->getUserPage(), $userObj->getName() ); |
224 | } |
225 | $links = ''; |
226 | $nt = $userObj->getUserPage(); |
227 | $talk = $nt->getTalkPage(); |
228 | if ( $talk ) { |
229 | $tools = SpecialContributions::getUserLinks( |
230 | $this, |
231 | $userObj, |
232 | $this->permissionManager, |
233 | $this->getHookRunner() |
234 | ); |
235 | |
236 | $contributionsLink = $linkRenderer->makeKnownLink( |
237 | SpecialPage::getTitleFor( 'Contributions', $nt->getDBkey() ), |
238 | $this->msg( 'sp-deletedcontributions-contribs' )->text() |
239 | ); |
240 | if ( isset( $tools['deletedcontribs'] ) ) { |
241 | // Swap out the deletedcontribs link for our contribs one |
242 | $tools = wfArrayInsertAfter( |
243 | $tools, [ 'contribs' => $contributionsLink ], 'deletedcontribs' ); |
244 | unset( $tools['deletedcontribs'] ); |
245 | } else { |
246 | $tools['contribs'] = $contributionsLink; |
247 | } |
248 | |
249 | $links = $this->getLanguage()->pipeList( $tools ); |
250 | |
251 | // Show a note if the user is blocked and display the last block log entry. |
252 | $block = $this->blockStore->newFromTarget( $userObj, $userObj ); |
253 | if ( $block !== null && $block->getType() != Block::TYPE_AUTO ) { |
254 | if ( $block->getType() == Block::TYPE_RANGE ) { |
255 | $nt = $this->namespaceInfo->getCanonicalName( NS_USER ) |
256 | . ':' . $block->getTargetName(); |
257 | } |
258 | |
259 | // LogEventsList::showLogExtract() wants the first parameter by ref |
260 | $out = $this->getOutput(); |
261 | LogEventsList::showLogExtract( |
262 | $out, |
263 | 'block', |
264 | $nt, |
265 | '', |
266 | [ |
267 | 'lim' => 1, |
268 | 'showIfEmpty' => false, |
269 | 'msgKey' => [ |
270 | 'sp-contributions-blocked-notice', |
271 | $userObj->getName() # Support GENDER in 'sp-contributions-blocked-notice' |
272 | ], |
273 | 'offset' => '' # don't use $this->getRequest() parameter offset |
274 | ] |
275 | ); |
276 | } |
277 | } |
278 | |
279 | return $this->msg( 'contribsub2' )->rawParams( $user, $links )->params( $userObj->getName() ); |
280 | } |
281 | |
282 | /** |
283 | * Generates the namespace selector form with hidden attributes. |
284 | */ |
285 | private function getForm() { |
286 | $opts = $this->mOpts; |
287 | |
288 | $formDescriptor = [ |
289 | 'target' => [ |
290 | 'type' => 'user', |
291 | 'name' => 'target', |
292 | 'label-message' => 'sp-contributions-username', |
293 | 'default' => $opts->getValue( 'target' ), |
294 | 'ipallowed' => true, |
295 | ], |
296 | |
297 | 'namespace' => [ |
298 | 'type' => 'namespaceselect', |
299 | 'name' => 'namespace', |
300 | 'label-message' => 'namespace', |
301 | 'all' => '', |
302 | ], |
303 | ]; |
304 | |
305 | HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ) |
306 | ->setWrapperLegendMsg( 'sp-contributions-search' ) |
307 | ->setSubmitTextMsg( 'sp-contributions-submit' ) |
308 | // prevent setting subpage and 'target' parameter at the same time |
309 | ->setTitle( $this->getPageTitle() ) |
310 | ->setMethod( 'get' ) |
311 | ->prepareForm() |
312 | ->displayForm( false ); |
313 | } |
314 | |
315 | /** |
316 | * Return an array of subpages beginning with $search that this special page will accept. |
317 | * |
318 | * @param string $search Prefix to search for |
319 | * @param int $limit Maximum number of results to return (usually 10) |
320 | * @param int $offset Number of results to skip (usually 0) |
321 | * @return string[] Matching subpages |
322 | */ |
323 | public function prefixSearchSubpages( $search, $limit, $offset ) { |
324 | $search = $this->userNameUtils->getCanonical( $search ); |
325 | if ( !$search ) { |
326 | // No prefix suggestion for invalid user |
327 | return []; |
328 | } |
329 | // Autocomplete subpage as user list - public to allow caching |
330 | return $this->userNamePrefixSearch |
331 | ->search( UserNamePrefixSearch::AUDIENCE_PUBLIC, $search, $limit, $offset ); |
332 | } |
333 | |
334 | protected function getGroupName() { |
335 | return 'users'; |
336 | } |
337 | } |
338 | |
339 | /** @deprecated class alias since 1.41 */ |
340 | class_alias( SpecialDeletedContributions::class, 'SpecialDeletedContributions' ); |