SpecialDeletedContributions.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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. use MediaWiki\Block\DatabaseBlock;
  24. use MediaWiki\MediaWikiServices;
  25. /**
  26. * Implements Special:DeletedContributions to display archived revisions
  27. * @ingroup SpecialPage
  28. */
  29. class SpecialDeletedContributions extends SpecialPage {
  30. /** @var FormOptions */
  31. protected $mOpts;
  32. function __construct() {
  33. parent::__construct( 'DeletedContributions', 'deletedhistory' );
  34. }
  35. /**
  36. * Special page "deleted user contributions".
  37. * Shows a list of the deleted contributions of a user.
  38. *
  39. * @param string $par (optional) user name of the user for which to show the contributions
  40. */
  41. function execute( $par ) {
  42. $this->setHeaders();
  43. $this->outputHeader();
  44. $this->checkPermissions();
  45. $this->addHelpLink( 'Help:User contributions' );
  46. $out = $this->getOutput();
  47. $out->setPageTitle( $this->msg( 'deletedcontributions-title' ) );
  48. $opts = new FormOptions();
  49. $opts->add( 'target', '' );
  50. $opts->add( 'namespace', '' );
  51. $opts->add( 'limit', 20 );
  52. $opts->fetchValuesFromRequest( $this->getRequest() );
  53. $opts->validateIntBounds( 'limit', 0, $this->getConfig()->get( 'QueryPageDefaultLimit' ) );
  54. if ( $par !== null ) {
  55. // Beautify the username
  56. $par = User::getCanonicalName( $par, false );
  57. $opts->setValue( 'target', (string)$par );
  58. }
  59. $ns = $opts->getValue( 'namespace' );
  60. if ( $ns !== null && $ns !== '' ) {
  61. $opts->setValue( 'namespace', intval( $ns ) );
  62. }
  63. $this->mOpts = $opts;
  64. $target = trim( $opts->getValue( 'target' ) );
  65. if ( !strlen( $target ) ) {
  66. $this->getForm();
  67. return;
  68. }
  69. $userObj = User::newFromName( $target, false );
  70. if ( !$userObj ) {
  71. $this->getForm();
  72. return;
  73. }
  74. $this->getSkin()->setRelevantUser( $userObj );
  75. $target = $userObj->getName();
  76. $out->addSubtitle( $this->getSubTitle( $userObj ) );
  77. $this->getForm();
  78. $pager = new DeletedContribsPager( $this->getContext(), $target, $opts->getValue( 'namespace' ),
  79. $this->getLinkRenderer() );
  80. if ( !$pager->getNumRows() ) {
  81. $out->addWikiMsg( 'nocontribs' );
  82. return;
  83. }
  84. # Show a message about replica DB lag, if applicable
  85. $lag = $pager->getDatabase()->getSessionLagStatus()['lag'];
  86. if ( $lag > 0 ) {
  87. $out->showLagWarning( $lag );
  88. }
  89. $out->addHTML(
  90. '<p>' . $pager->getNavigationBar() . '</p>' .
  91. $pager->getBody() .
  92. '<p>' . $pager->getNavigationBar() . '</p>' );
  93. # If there were contributions, and it was a valid user or IP, show
  94. # the appropriate "footer" message - WHOIS tools, etc.
  95. $message = IP::isIPAddress( $target ) ?
  96. 'sp-contributions-footer-anon' :
  97. 'sp-contributions-footer';
  98. if ( !$this->msg( $message )->isDisabled() ) {
  99. $out->wrapWikiMsg(
  100. "<div class='mw-contributions-footer'>\n$1\n</div>",
  101. [ $message, $target ]
  102. );
  103. }
  104. }
  105. /**
  106. * Generates the subheading with links
  107. * @param User $userObj User object for the target
  108. * @return string Appropriately-escaped HTML to be output literally
  109. */
  110. function getSubTitle( $userObj ) {
  111. $linkRenderer = $this->getLinkRenderer();
  112. if ( $userObj->isAnon() ) {
  113. $user = htmlspecialchars( $userObj->getName() );
  114. } else {
  115. $user = $linkRenderer->makeLink( $userObj->getUserPage(), $userObj->getName() );
  116. }
  117. $links = '';
  118. $nt = $userObj->getUserPage();
  119. $talk = $nt->getTalkPage();
  120. if ( $talk ) {
  121. $tools = SpecialContributions::getUserLinks( $this, $userObj );
  122. $contributionsLink = $linkRenderer->makeKnownLink(
  123. SpecialPage::getTitleFor( 'Contributions', $nt->getDBkey() ),
  124. $this->msg( 'sp-deletedcontributions-contribs' )->text()
  125. );
  126. if ( isset( $tools['deletedcontribs'] ) ) {
  127. // Swap out the deletedcontribs link for our contribs one
  128. $tools = wfArrayInsertAfter(
  129. $tools, [ 'contribs' => $contributionsLink ], 'deletedcontribs' );
  130. unset( $tools['deletedcontribs'] );
  131. } else {
  132. $tools['contribs'] = $contributionsLink;
  133. }
  134. $links = $this->getLanguage()->pipeList( $tools );
  135. // Show a note if the user is blocked and display the last block log entry.
  136. $block = DatabaseBlock::newFromTarget( $userObj, $userObj );
  137. if ( !is_null( $block ) && $block->getType() != DatabaseBlock::TYPE_AUTO ) {
  138. if ( $block->getType() == DatabaseBlock::TYPE_RANGE ) {
  139. $nt = MediaWikiServices::getInstance()->getNamespaceInfo()->
  140. getCanonicalName( NS_USER ) . ':' . $block->getTarget();
  141. }
  142. // LogEventsList::showLogExtract() wants the first parameter by ref
  143. $out = $this->getOutput();
  144. LogEventsList::showLogExtract(
  145. $out,
  146. 'block',
  147. $nt,
  148. '',
  149. [
  150. 'lim' => 1,
  151. 'showIfEmpty' => false,
  152. 'msgKey' => [
  153. 'sp-contributions-blocked-notice',
  154. $userObj->getName() # Support GENDER in 'sp-contributions-blocked-notice'
  155. ],
  156. 'offset' => '' # don't use $this->getRequest() parameter offset
  157. ]
  158. );
  159. }
  160. }
  161. return $this->msg( 'contribsub2' )->rawParams( $user, $links )->params( $userObj->getName() );
  162. }
  163. /**
  164. * Generates the namespace selector form with hidden attributes.
  165. */
  166. function getForm() {
  167. $opts = $this->mOpts;
  168. $formDescriptor = [
  169. 'target' => [
  170. 'type' => 'user',
  171. 'name' => 'target',
  172. 'label-message' => 'sp-contributions-username',
  173. 'default' => $opts->getValue( 'target' ),
  174. 'ipallowed' => true,
  175. ],
  176. 'namespace' => [
  177. 'type' => 'namespaceselect',
  178. 'name' => 'namespace',
  179. 'label-message' => 'namespace',
  180. 'all' => '',
  181. ],
  182. ];
  183. HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
  184. ->setWrapperLegendMsg( 'sp-contributions-search' )
  185. ->setSubmitTextMsg( 'sp-contributions-submit' )
  186. // prevent setting subpage and 'target' parameter at the same time
  187. ->setAction( $this->getPageTitle()->getLocalURL() )
  188. ->setMethod( 'get' )
  189. ->prepareForm()
  190. ->displayForm( false );
  191. }
  192. /**
  193. * Return an array of subpages beginning with $search that this special page will accept.
  194. *
  195. * @param string $search Prefix to search for
  196. * @param int $limit Maximum number of results to return (usually 10)
  197. * @param int $offset Number of results to skip (usually 0)
  198. * @return string[] Matching subpages
  199. */
  200. public function prefixSearchSubpages( $search, $limit, $offset ) {
  201. $user = User::newFromName( $search );
  202. if ( !$user ) {
  203. // No prefix suggestion for invalid user
  204. return [];
  205. }
  206. // Autocomplete subpage as user list - public to allow caching
  207. return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
  208. }
  209. protected function getGroupName() {
  210. return 'users';
  211. }
  212. }