Merge "(bug 12701) Use diff of all unseen revisions in the "new messages" bar."
authorAaron Schulz <aschulz@wikimedia.org>
Thu, 9 Aug 2012 16:59:57 +0000 (16:59 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 9 Aug 2012 16:59:57 +0000 (16:59 +0000)
includes/Skin.php
includes/Title.php
includes/User.php
includes/WikiPage.php
languages/messages/MessagesEn.php
languages/messages/MessagesQqq.php
maintenance/language/messages.inc

index f4960c1..22cc0c9 100644 (file)
@@ -72,7 +72,7 @@ abstract class Skin extends ContextSource {
                }
                return $wgValidSkinNames;
        }
+
        /**
         * Fetch the skinname messages for available skins.
         * @return array of strings
@@ -1342,29 +1342,58 @@ abstract class Skin extends ContextSource {
                $ntl = '';
 
                if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) {
-                       $userTitle = $this->getUser()->getUserPage();
-                       $userTalkTitle = $userTitle->getTalkPage();
+                       $userTalkTitle = $this->getUser()->getTalkPage();
 
                        if ( !$userTalkTitle->equals( $out->getTitle() ) ) {
+                               $lastSeenRev = isset( $newtalks[0]['rev'] ) ? $newtalks[0]['rev'] : null;
+                               $nofAuthors = 0;
+                               if ( $lastSeenRev !== null ) {
+                                       $plural = true; // Default if we have a last seen revision: if unknown, use plural
+                                       $latestRev = Revision::newFromTitle ($userTalkTitle);
+                                       if ( $latestRev !== null ) {
+                                               // Singular if only 1 unseen revision, plural if several unseen revisions.\r
+                                               $plural = $latestRev->getParentId() !== $lastSeenRev->getId();\r
+                                               $nofAuthors = $userTalkTitle->countAuthorsBetween( $lastSeenRev, $latestRev, 10, 'include_new' );
+                                       }
+                               } else {
+                                       // Singular if no revision -> diff link will show latest change only in any case
+                                       $plural = false;
+                               }
+                               $plural = $plural ? 2 : 1;
+                               // 2 signifies "more than one revision". We don't know how many, and even if we did,
+                               // the number of revisions or authors is not necessarily the same as the number of
+                               // "messages".
                                $newMessagesLink = Linker::linkKnown(
                                        $userTalkTitle,
-                                       $this->msg( 'newmessageslink' )->escaped(),
+                                       $this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(),
                                        array(),
                                        array( 'redirect' => 'no' )
                                );
 
                                $newMessagesDiffLink = Linker::linkKnown(
                                        $userTalkTitle,
-                                       $this->msg( 'newmessagesdifflink' )->escaped(),
+                                       $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(),
                                        array(),
-                                       array( 'diff' => 'cur' )
+                                       $lastSeenRev !== null
+                                               ? array( 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' )
+                                               : array( 'diff' => 'cur' )
                                );
 
-                               $ntl = $this->msg(
-                                       'youhavenewmessages',
-                                       $newMessagesLink,
-                                       $newMessagesDiffLink
-                               )->text();
+                               if ( $nofAuthors >= 1 && $nofAuthors <= 10 ) {
+                                       $ntl = $this->msg(
+                                               'youhavenewmessagesfromusers',
+                                               $newMessagesLink,
+                                               $newMessagesDiffLink
+                                       )->numParams( $nofAuthors );
+                               } else {
+                                       // $nofAuthors === 11 signifies "11 or more" ("more than 10")
+                                       $ntl = $this->msg(
+                                               $nofAuthors > 10 ? 'youhavenewmessagesmanyusers' : 'youhavenewmessages',
+                                               $newMessagesLink,
+                                               $newMessagesDiffLink
+                                       );
+                               }
+                               $ntl = $ntl->text();
                                # Disable Squid cache
                                $out->setSquidMaxage( 0 );
                        }
index 4f7984e..642f8dd 100644 (file)
@@ -825,7 +825,7 @@ class Title {
 
        /**
         * Returns true if the title is inside the specified namespace.
-        * 
+        *
         * Please make use of this instead of comparing to getNamespace()
         * This function is much more resistant to changes we may make
         * to namespaces than code that makes direct comparisons.
@@ -1882,7 +1882,7 @@ class Title {
                                        $title_protection['pt_create_perm'] = 'protect'; // B/C
                                }
                                if( $title_protection['pt_create_perm'] == '' ||
-                                       !$user->isAllowed( $title_protection['pt_create_perm'] ) ) 
+                                       !$user->isAllowed( $title_protection['pt_create_perm'] ) )
                                {
                                        $errors[] = array( 'titleprotected', User::whoIs( $title_protection['pt_user'] ), $title_protection['pt_reason'] );
                                }
@@ -4079,30 +4079,60 @@ class Title {
        }
 
        /**
-        * Get the number of authors between the given revision IDs.
+        * Get the number of authors between the given revisions or revision IDs.
         * Used for diffs and other things that really need it.
         *
-        * @param $old int|Revision Old revision or rev ID (first before range)
-        * @param $new int|Revision New revision or rev ID (first after range)
-        * @param $limit Int Maximum number of authors
-        * @return Int Number of revision authors between these revisions.
-        */
-       public function countAuthorsBetween( $old, $new, $limit ) {
+        * @param $old int|Revision Old revision or rev ID (first before range by default)
+        * @param $new int|Revision New revision or rev ID (first after range by default)
+        * @param $limit int Maximum number of authors
+        * @param $options string|array (Optional): Single option, or an array of options:
+        *     'include_old' Include $old in the range; $new is excluded.
+        *     'include_new' Include $new in the range; $old is excluded.
+        *     'include_both' Include both $old and $new in the range.
+        *     Unknown option values are ignored.
+        * @return int Number of revision authors in the range; zero if not both revisions exist
+        */
+       public function countAuthorsBetween( $old, $new, $limit, $options = array() ) {
                if ( !( $old instanceof Revision ) ) {
                        $old = Revision::newFromTitle( $this, (int)$old );
                }
                if ( !( $new instanceof Revision ) ) {
                        $new = Revision::newFromTitle( $this, (int)$new );
                }
+               // XXX: what if Revision objects are passed in, but they don't refer to this title?
+               // Add $old->getPage() != $new->getPage() || $old->getPage() != $this->getArticleID()
+               // in the sanity check below?
                if ( !$old || !$new ) {
                        return 0; // nothing to compare
                }
+               $old_cmp = '>';
+               $new_cmp = '<';
+               $options = (array) $options;
+               if ( in_array( 'include_old', $options ) ) {
+                       $old_cmp = '>=';
+               }
+               if ( in_array( 'include_new', $options ) ) {\r
+                       $new_cmp = '<=';\r
+               }\r
+               if ( in_array( 'include_both', $options ) ) {\r
+                       $old_cmp = '>=';\r
+                       $new_cmp = '<=';
+               }
+               // No DB query needed if $old and $new are the same or successive revisions:
+               if ( $old->getId() === $new->getId() ) {
+                       return ( $old_cmp === '>' && $new_cmp === '<' ) ? 0 : 1;
+               } else if ( $old->getId() === $new->getParentId() ) {
+                       if ( $old_cmp === '>' || $new_cmp === '<' ) {
+                               return ( $old_cmp === '>' && $new_cmp === '<' ) ? 0 : 1;
+                       }
+                       return ( $old->getRawUserText() === $new->getRawUserText() ) ? 1 : 2;
+               }\r
                $dbr = wfGetDB( DB_SLAVE );
                $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
                        array(
                                'rev_page' => $this->getArticleID(),
-                               'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
-                               'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
+                               "rev_timestamp $old_cmp " . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
+                               "rev_timestamp $new_cmp " . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
                        ), __METHOD__,
                        array( 'LIMIT' => $limit + 1 ) // add one so caller knows it was truncated
                );
index aef49fc..d2ebabb 100644 (file)
@@ -1789,14 +1789,20 @@ class User {
         */
        public function getNewMessageLinks() {
                $talks = array();
-               if( !wfRunHooks( 'UserRetrieveNewTalks', array( &$this, &$talks ) ) )
+               if( !wfRunHooks( 'UserRetrieveNewTalks', array( &$this, &$talks ) ) ) {
                        return $talks;
-
-               if( !$this->getNewtalk() )
+               } elseif( !$this->getNewtalk() ) {
                        return array();
-               $up = $this->getUserPage();
-               $utp = $up->getTalkPage();
-               return array( array( 'wiki' => wfWikiID(), 'link' => $utp->getLocalURL() ) );
+               }
+               $utp = $this->getTalkPage();
+               $dbr = wfGetDB( DB_SLAVE );
+               // Get the "last viewed rev" timestamp from the oldest message notification
+               $timestamp = $dbr->selectField( 'user_newtalk',
+                       'MIN(user_last_timestamp)',
+                       $this->isAnon() ? array( 'user_ip' => $this->getName() ) : array( 'user_id' => $this->getID() ),
+                       __METHOD__ );
+               $rev = $timestamp ? Revision::loadFromTimestamp( $dbr, $utp, $timestamp ) : null;
+               return array( array( 'wiki' => wfWikiID(), 'link' => $utp->getLocalURL(), 'rev' => $rev ) );
        }
 
        /**
@@ -1823,12 +1829,17 @@ class User {
         * Add or update the new messages flag
         * @param $field String 'user_ip' for anonymous users, 'user_id' otherwise
         * @param $id String|Int User's IP address for anonymous users, User ID otherwise
+        * @param $curRev Revision new, as yet unseen revision of the user talk page. Ignored if null.
         * @return Bool True if successful, false otherwise
         */
-       protected function updateNewtalk( $field, $id ) {
+       protected function updateNewtalk( $field, $id, $curRev = null ) {
+               // Get timestamp of the talk page revision prior to the current one
+               $prevRev = $curRev ? $curRev->getPrevious() : false;
+               $ts = $prevRev ? $prevRev->getTimestamp() : null;
+               // Mark the user as having new messages since this revision
                $dbw = wfGetDB( DB_MASTER );
                $dbw->insert( 'user_newtalk',
-                       array( $field => $id ),
+                       array( $field => $id, 'user_last_timestamp' => $dbw->timestampOrNull( $ts ) ),
                        __METHOD__,
                        'IGNORE' );
                if ( $dbw->affectedRows() ) {
@@ -1863,8 +1874,9 @@ class User {
        /**
         * Update the 'You have new messages!' status.
         * @param $val Bool Whether the user has new messages
+        * @param $curRev Revision new, as yet unseen revision of the user talk page. Ignored if null or !$val.
         */
-       public function setNewtalk( $val ) {
+       public function setNewtalk( $val, $curRev = null ) {
                if( wfReadOnly() ) {
                        return;
                }
@@ -1882,7 +1894,7 @@ class User {
                global $wgMemc;
 
                if( $val ) {
-                       $changed = $this->updateNewtalk( $field, $id );
+                       $changed = $this->updateNewtalk( $field, $id, $curRev );
                } else {
                        $changed = $this->deleteNewtalk( $field, $id );
                }
index 764a831..9dc9c2c 100644 (file)
@@ -1788,9 +1788,9 @@ class WikiPage extends Page implements IDBAccessObject {
                                        wfDebug( __METHOD__ . ": invalid username\n" );
                                } elseif ( User::isIP( $shortTitle ) ) {
                                        // An anonymous user
-                                       $other->setNewtalk( true );
+                                       $other->setNewtalk( true, $revision );
                                } elseif ( $other->isLoggedIn() ) {
-                                       $other->setNewtalk( true );
+                                       $other->setNewtalk( true, $revision );
                                } else {
                                        wfDebug( __METHOD__ . ": don't need to notify a nonexistent user\n" );
                                }
index 91de9a1..afb4319 100644 (file)
@@ -911,6 +911,10 @@ See [[Special:Version|version page]].',
 'youhavenewmessages'           => 'You have $1 ($2).',
 'newmessageslink'              => 'new messages',
 'newmessagesdifflink'          => 'last change',
+'youhavenewmessagesfromusers'  => 'You have $1 from {{PLURAL:$3|another user|$3 users}} ($2).',
+'youhavenewmessagesmanyusers'  => 'You have $1 from many users ($2).',
+'newmessageslinkplural'        => '{{PLURAL:$1|a new message|new messages}}', # don't rely on the value of $1, it's 1 for singular and 2 for "more than one"
+'newmessagesdifflinkplural'    => 'last {{PLURAL:$1|change|changes}}', # don't rely on the value of $1, it's 1 for singular and 2 for "more than one"
 'youhavenewmessagesmulti'      => 'You have new messages on $1',
 'newtalkseparator'             => ',&#32;', # do not translate or duplicate this message to other languages
 'editsection'                  => 'edit',
index c98d802..2ccbd67 100644 (file)
@@ -548,6 +548,18 @@ The format is: "{{int:youhavenewmessages| [[MediaWiki:Newmessageslink/{{SUBPAGEN
 
 {{Identical|New messages}}',
 'newmessagesdifflink' => 'This is the second link displayed in an orange rectangle when a user gets a message on his talk page. Used in message {{msg-mw|youhavenewmessages}} (as parameter $2).',
+'youhavenewmessagesfromusers' => 'New talk inidcator message: the message appearing when someone edited your user talk page.
+The message takes three parameters, {{msg-mw|newmessageslinkplural}}, {{msg-mw|newmessagesdifflinkplurl}}, and the number of authors
+that have edited the talk page since the owning user last viewed it.',
+'youhavenewmessagesmanyusers' => 'New talk inidcator message: the message appearing when someone edited your user talk page.
+Like {{msg-mw|youhavenewmessages}}, but getting {{msg-mw|newmessageslinkplural}} and {{msg-mw|newmessagesdifflinkplurl}} as parameters $1 and $2, respectively. Used when more than 10 users
+edited the user talk page since the owning user last viewed it.',
+'newmessageslinkplural' => 'Like {{msg-mw|newmessageslink}} but supporting pluralization. Used in message {{msg-mw|youhavenewmessagesfromusers}} (as parameter $1).
+This message itself takes one parameter, $1, which is 1 if there was one new edit, or 2 if there was more than one new edit
+since the last time the user has seen his or her talk page.',
+'newmessagesdifflinkplural' => 'Like {{msg-mw|newmessagesdifflink}} but supporting pluralization. Used in message {{msg-mw|youhavenewmessagesfromusers}} (as parameter $2).
+This message itself takes one parameter, $1, which is 1 if there was one new edit, or 2 if there was more than one new edit
+since the last time the user has seen his or her talk page.',
 'youhavenewmessagesmulti' => 'The alternative of {{msg|youhavenewmessages}} as used on wikis with a special setup so they can receive the "new message" notice on other wikis as well. Used on [http://www.wikia.com/ Wikia].
 The format is: "{{int:youhavenewmessagesmulti| [[MediaWiki:Newmessageslink/{{SUBPAGENAME}}|{{int:newmessageslink}}]]}}"',
 'editsection' => 'Display name of link to edit a section on a content page. Example: [{{MediaWiki:Editsection}}].
index 11601e6..be4970d 100644 (file)
@@ -306,6 +306,10 @@ $wgMessageStructure = array(
                'youhavenewmessages',
                'newmessageslink',
                'newmessagesdifflink',
+               'youhavenewmessagesfromusers',
+               'youhavenewmessagesmanyusers',
+               'newmessageslinkplural',
+               'newmessagesdifflinkplural',
                'youhavenewmessagesmulti',
                'newtalkseparator',
                'editsection',