RollbackAction.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. <?php
  2. /**
  3. * Edit rollback user interface
  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
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  18. *
  19. * @file
  20. * @ingroup Actions
  21. */
  22. use MediaWiki\Revision\RevisionRecord;
  23. /**
  24. * User interface for the rollback action
  25. *
  26. * @ingroup Actions
  27. */
  28. class RollbackAction extends FormAction {
  29. public function getName() {
  30. return 'rollback';
  31. }
  32. public function getRestriction() {
  33. return 'rollback';
  34. }
  35. protected function usesOOUI() {
  36. return true;
  37. }
  38. protected function getDescription() {
  39. return '';
  40. }
  41. public function doesWrites() {
  42. return true;
  43. }
  44. public function onSuccess() {
  45. return false;
  46. }
  47. public function onSubmit( $data ) {
  48. return false;
  49. }
  50. protected function alterForm( HTMLForm $form ) {
  51. $form->setWrapperLegendMsg( 'confirm-rollback-top' );
  52. $form->setSubmitTextMsg( 'confirm-rollback-button' );
  53. $form->setTokenSalt( 'rollback' );
  54. $from = $this->getRequest()->getVal( 'from' );
  55. if ( $from === null ) {
  56. throw new BadRequestError( 'rollbackfailed', 'rollback-missingparam' );
  57. }
  58. foreach ( [ 'from', 'bot', 'hidediff', 'summary', 'token' ] as $param ) {
  59. $val = $this->getRequest()->getVal( $param );
  60. if ( $val !== null ) {
  61. $form->addHiddenField( $param, $val );
  62. }
  63. }
  64. }
  65. /**
  66. * @throws ErrorPageError
  67. * @throws ReadOnlyError
  68. * @throws ThrottledError
  69. */
  70. public function show() {
  71. if ( $this->getUser()->getOption( 'showrollbackconfirmation' ) == false ||
  72. $this->getRequest()->wasPosted() ) {
  73. $this->handleRollbackRequest();
  74. } else {
  75. $this->showRollbackConfirmationForm();
  76. }
  77. }
  78. public function handleRollbackRequest() {
  79. $this->enableTransactionalTimelimit();
  80. $request = $this->getRequest();
  81. $user = $this->getUser();
  82. $from = $request->getVal( 'from' );
  83. $rev = $this->page->getRevision();
  84. if ( $from === null ) {
  85. throw new ErrorPageError( 'rollbackfailed', 'rollback-missingparam' );
  86. }
  87. if ( !$rev ) {
  88. throw new ErrorPageError( 'rollbackfailed', 'rollback-missingrevision' );
  89. }
  90. if ( $from !== $rev->getUserText() ) {
  91. throw new ErrorPageError( 'rollbackfailed', 'alreadyrolled', [
  92. $this->getTitle()->getPrefixedText(),
  93. $from,
  94. $rev->getUserText()
  95. ] );
  96. }
  97. $data = null;
  98. $errors = $this->page->doRollback(
  99. $from,
  100. $request->getText( 'summary' ),
  101. $request->getVal( 'token' ),
  102. $request->getBool( 'bot' ),
  103. $data,
  104. $this->getUser()
  105. );
  106. if ( in_array( [ 'actionthrottledtext' ], $errors ) ) {
  107. throw new ThrottledError;
  108. }
  109. if ( $this->hasRollbackRelatedErrors( $errors ) ) {
  110. $this->getOutput()->setPageTitle( $this->msg( 'rollbackfailed' ) );
  111. $errArray = $errors[0];
  112. $errMsg = array_shift( $errArray );
  113. $this->getOutput()->addWikiMsgArray( $errMsg, $errArray );
  114. if ( isset( $data['current'] ) ) {
  115. /** @var Revision $current */
  116. $current = $data['current'];
  117. if ( $current->getComment() != '' ) {
  118. $this->getOutput()->addWikiMsg(
  119. 'editcomment',
  120. Message::rawParam(
  121. Linker::formatComment( $current->getComment() )
  122. )
  123. );
  124. }
  125. }
  126. return;
  127. }
  128. # NOTE: Permission errors already handled by Action::checkExecute.
  129. if ( $errors == [ [ 'readonlytext' ] ] ) {
  130. throw new ReadOnlyError;
  131. }
  132. # XXX: Would be nice if ErrorPageError could take multiple errors, and/or a status object.
  133. # Right now, we only show the first error
  134. foreach ( $errors as $error ) {
  135. throw new ErrorPageError( 'rollbackfailed', $error[0], array_slice( $error, 1 ) );
  136. }
  137. /** @var Revision $current */
  138. $current = $data['current'];
  139. $target = $data['target'];
  140. $newId = $data['newid'];
  141. $this->getOutput()->setPageTitle( $this->msg( 'actioncomplete' ) );
  142. $this->getOutput()->setRobotPolicy( 'noindex,nofollow' );
  143. $old = Linker::revUserTools( $current );
  144. $new = Linker::revUserTools( $target );
  145. $this->getOutput()->addHTML(
  146. $this->msg( 'rollback-success' )
  147. ->rawParams( $old, $new )
  148. ->params( $current->getUserText( RevisionRecord::FOR_THIS_USER, $user ) )
  149. ->params( $target->getUserText( RevisionRecord::FOR_THIS_USER, $user ) )
  150. ->parseAsBlock()
  151. );
  152. if ( $user->getBoolOption( 'watchrollback' ) ) {
  153. $user->addWatch( $this->page->getTitle(), User::IGNORE_USER_RIGHTS );
  154. }
  155. $this->getOutput()->returnToMain( false, $this->getTitle() );
  156. if ( !$request->getBool( 'hidediff', false ) &&
  157. !$this->getUser()->getBoolOption( 'norollbackdiff' )
  158. ) {
  159. $contentHandler = $current->getContentHandler();
  160. $de = $contentHandler->createDifferenceEngine(
  161. $this->getContext(),
  162. $current->getId(),
  163. $newId,
  164. false,
  165. true
  166. );
  167. $de->showDiff( '', '' );
  168. }
  169. }
  170. /**
  171. * Enables transactional time limit for POST and GET requests to RollbackAction
  172. * @throws ConfigException
  173. */
  174. private function enableTransactionalTimelimit() {
  175. // If Rollbacks are made POST-only, use $this->useTransactionalTimeLimit()
  176. wfTransactionalTimeLimit();
  177. if ( !$this->getRequest()->wasPosted() ) {
  178. /**
  179. * We apply the higher POST limits on GET requests
  180. * to prevent logstash.wikimedia.org from being spammed
  181. */
  182. $fname = __METHOD__;
  183. $trxLimits = $this->context->getConfig()->get( 'TrxProfilerLimits' );
  184. $trxProfiler = Profiler::instance()->getTransactionProfiler();
  185. $trxProfiler->redefineExpectations( $trxLimits['POST'], $fname );
  186. DeferredUpdates::addCallableUpdate( function () use ( $trxProfiler, $trxLimits, $fname
  187. ) {
  188. $trxProfiler->redefineExpectations( $trxLimits['PostSend-POST'], $fname );
  189. } );
  190. }
  191. }
  192. private function showRollbackConfirmationForm() {
  193. $form = $this->getForm();
  194. if ( $form->show() ) {
  195. $this->onSuccess();
  196. }
  197. }
  198. protected function getFormFields() {
  199. return [
  200. 'intro' => [
  201. 'type' => 'info',
  202. 'vertical-label' => true,
  203. 'raw' => true,
  204. 'default' => $this->msg( 'confirm-rollback-bottom' )->parse()
  205. ]
  206. ];
  207. }
  208. private function hasRollbackRelatedErrors( array $errors ) {
  209. return isset( $errors[0][0] ) &&
  210. ( $errors[0][0] == 'alreadyrolled' ||
  211. $errors[0][0] == 'cantrollback'
  212. );
  213. }
  214. }