(bug 39397) Only show rollback links if they work
authorMarius Hoch <hoo@online.de>
Thu, 1 Nov 2012 20:04:12 +0000 (21:04 +0100)
committerMarius Hoch <hoo@online.de>
Wed, 9 Jan 2013 16:04:44 +0000 (17:04 +0100)
To only show rollback links if they work I had to patch Linker
to have a function (Linker::rollbackData) which can verify
that the editor isn't the only editor of the page. Furthermore
it is checked that the user name or the text of the revision
we might rollback to isn't deleted. Due to the fact that I've
altered the already existing method which showed how many edits
a rollback will revert for that, this wont affect the performance.

Change-Id: I5d1adec993370c39ae8c5c712edd919d456441c6

RELEASE-NOTES-1.21
includes/DefaultSettings.php
includes/Linker.php
includes/actions/HistoryAction.php
includes/diff/DifferenceEngine.php

index 7d0ad48..de545da 100644 (file)
@@ -75,6 +75,7 @@ production.
   dropExtensionIndex and renameExtensionIndex.
 * New preference type - 'api'. Preferences of this type are not shown on
   Special:Preferences, but are still available via the action=options API.
+* (bug 39397) Hide rollback link if a user is the only contributor of the page.
 
 === Bug fixes in 1.21 ===
 * (bug 40353) SpecialDoubleRedirect should support interwiki redirects.
index c529ef7..f8738dd 100644 (file)
@@ -2832,7 +2832,8 @@ $wgSend404Code = true;
 /**
  * The $wgShowRollbackEditCount variable is used to show how many edits will be
  * rollback. The numeric value of the varible are the limit up to are counted.
- * If the value is false or 0, the edits are not counted.
+ * If the value is false or 0, the edits are not counted. Disabling this will
+ * furthermore prevent MediaWiki from hiding some useless rollback links.
  *
  * @since 1.20
  */
index 6b0d444..5c41b42 100644 (file)
@@ -1756,19 +1756,101 @@ class Linker {
         * changes, so this allows sysops to combat a busy vandal without bothering
         * other users.
         *
+        * If the option verify is set this function will return the link only in case the
+        * revision can be reverted. Please note that due to performance limitations
+        * it might be assumed that a user isn't the only contributor of a page while
+        * (s)he is, which will lead to useless rollback links. Furthermore this wont
+        * work if $wgShowRollbackEditCount is disabled, so this can only function
+        * as an additional check.
+        *
+        * If the option noBrackets is set the rollback link wont be enclosed in []
+        *
         * @param $rev Revision object
         * @param $context IContextSource context to use or null for the main context.
+        * @param $options array
         * @return string
         */
-       public static function generateRollback( $rev, IContextSource $context = null ) {
+       public static function generateRollback( $rev, IContextSource $context = null, $options = array( 'verify' ) ) {
                if ( $context === null ) {
                        $context = RequestContext::getMain();
                }
+               $editCount = false;
+               if ( in_array( 'verify', $options ) ) {
+                       $editCount = self::getRollbackEditCount( $rev, true );
+                       if ( $editCount === false ) {
+                               return '';
+                       }
+               }
 
-               return '<span class="mw-rollback-link">'
-                       . $context->msg( 'brackets' )->rawParams(
-                               self::buildRollbackLink( $rev, $context ) )->plain()
-                       . '</span>';
+               $inner = self::buildRollbackLink( $rev, $context, $editCount );
+
+               if ( !in_array( 'noBrackets', $options ) ) {
+                       $inner = $context->msg( 'brackets' )->rawParams( $inner )->plain();
+               }
+
+               return '<span class="mw-rollback-link">' . $inner . '</span>';
+       }
+
+       /**
+        * This function will return the number of revisions which a rollback
+        * would revert and, if $verify is set it will verify that a revision
+        * can be reverted (that the user isn't the only contributor and the
+        * revision we might rollback to isn't deleted). These checks can only
+        * function as an additional check as this function only checks against
+        * the last $wgShowRollbackEditCount edits.
+        *
+        * Returns null if $wgShowRollbackEditCount is disabled or false if $verify
+        * is set and the user is the only contributor of the page.
+        *
+        * @param $rev Revision object
+        * @param $verify Bool Try to verfiy that this revision can really be rolled back
+        * @return integer|bool|null
+        */
+       public static function getRollbackEditCount( $rev, $verify ) {
+               global $wgShowRollbackEditCount;
+               if ( !is_int( $wgShowRollbackEditCount ) || !$wgShowRollbackEditCount > 0 ) {
+                       // Nothing has happened, indicate this by returning 'null'
+                       return null;
+               }
+
+               $dbr = wfGetDB( DB_SLAVE );
+
+               // Up to the value of $wgShowRollbackEditCount revisions are counted
+               $res = $dbr->select(
+                       'revision',
+                       array( 'rev_user_text', 'rev_deleted' ),
+                       // $rev->getPage() returns null sometimes
+                       array( 'rev_page' => $rev->getTitle()->getArticleID() ),
+                       __METHOD__,
+                       array(
+                               'USE INDEX' => array( 'revision' => 'page_timestamp' ),
+                               'ORDER BY' => 'rev_timestamp DESC',
+                               'LIMIT' => $wgShowRollbackEditCount + 1
+                       )
+               );
+
+               $editCount = 0;
+               $moreRevs = false;
+               foreach ( $res as $row ) {
+                       if ( $rev->getRawUserText() != $row->rev_user_text ) {
+                               if ( $verify && ( $row->rev_deleted & Revision::DELETED_TEXT || $row->rev_deleted & Revision::DELETED_USER ) ) {
+                                       // If the user or the text of the revision we might rollback to is deleted in some way we can't rollback
+                                       // Similar to the sanity checks in WikiPage::commitRollback
+                                       return false;
+                               }
+                               $moreRevs = true;
+                               break;
+                       }
+                       $editCount++;
+               }
+
+               if ( $verify && $editCount <= $wgShowRollbackEditCount && !$moreRevs ) {
+                       // We didn't find at least $wgShowRollbackEditCount revisions made by the current user
+                       // and there weren't any other revisions. That means that the current user is the only
+                       // editor, so we can't rollback
+                       return false;
+               }
+               return $editCount;
        }
 
        /**
@@ -1776,9 +1858,10 @@ class Linker {
         *
         * @param $rev Revision object
         * @param $context IContextSource context to use or null for the main context.
+        * @param $editCount integer Number of edits that would be reverted
         * @return String: HTML fragment
         */
-       public static function buildRollbackLink( $rev, IContextSource $context = null ) {
+       public static function buildRollbackLink( $rev, IContextSource $context = null, $editCount = false ) {
                global $wgShowRollbackEditCount, $wgMiserMode;
 
                // To config which pages are effected by miser mode
@@ -1810,25 +1893,8 @@ class Linker {
                }
 
                if( !$disableRollbackEditCount && is_int( $wgShowRollbackEditCount ) && $wgShowRollbackEditCount > 0 ) {
-                       $dbr = wfGetDB( DB_SLAVE );
-
-                       // Up to the value of $wgShowRollbackEditCount revisions are counted
-                       $res = $dbr->select( 'revision',
-                               array( 'rev_id', 'rev_user_text' ),
-                               // $rev->getPage() returns null sometimes
-                               array( 'rev_page' => $rev->getTitle()->getArticleID() ),
-                               __METHOD__,
-                               array(  'USE INDEX' => 'page_timestamp',
-                                       'ORDER BY' => 'rev_timestamp DESC',
-                                       'LIMIT' => $wgShowRollbackEditCount + 1 )
-                       );
-
-                       $editCount = 0;
-                       while( $row = $dbr->fetchObject( $res ) ) {
-                               if( $rev->getUserText() != $row->rev_user_text ) {
-                                       break;
-                               }
-                               $editCount++;
+                       if ( !is_numeric( $editCount ) ) {
+                               $editCount = self::getRollbackEditCount( $rev, false );
                        }
 
                        if( $editCount > $wgShowRollbackEditCount ) {
index c33423d..43a03db 100644 (file)
@@ -626,9 +626,12 @@ class HistoryPager extends ReverseChronologicalPager {
                # Rollback and undo links
                if ( $prevRev && $this->getTitle()->quickUserCan( 'edit', $user ) ) {
                        if ( $latest && $this->getTitle()->quickUserCan( 'rollback', $user ) ) {
-                               $this->preventClickjacking();
-                               $tools[] = '<span class="mw-rollback-link">' .
-                                       Linker::buildRollbackLink( $rev, $this->getContext() ) . '</span>';
+                               // Get a rollback link without the brackets
+                               $rollbackLink = Linker::generateRollback( $rev, $this->getContext(), array( 'verify', 'noBrackets' ) );
+                               if ( $rollbackLink ) {
+                                       $this->preventClickjacking();
+                                       $tools[] = $rollbackLink;
+                               }
                        }
 
                        if ( !$rev->isDeleted( Revision::DELETED_TEXT )
index 6fcf0e8..97553e6 100644 (file)
@@ -294,8 +294,11 @@ class DifferenceEngine extends ContextSource {
 
                        if ( $samePage && $this->mNewPage->quickUserCan( 'edit', $user ) ) {
                                if ( $this->mNewRev->isCurrent() && $this->mNewPage->userCan( 'rollback', $user ) ) {
-                                       $out->preventClickjacking();
-                                       $rollback = '&#160;&#160;&#160;' . Linker::generateRollback( $this->mNewRev, $this->getContext() );
+                                       $rollbackLink = Linker::generateRollback( $this->mNewRev, $this->getContext() );
+                                       if ( $rollbackLink ) {
+                                               $out->preventClickjacking();
+                                               $rollback = '&#160;&#160;&#160;' . $rollbackLink;
+                                       }
                                }
                                if ( !$this->mOldRev->isDeleted( Revision::DELETED_TEXT ) && !$this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) {
                                        $undoLink = ' ' . $this->msg( 'parentheses' )->rawParams(