New feature RevisionMove (bug 21312). Introducing SpecialRevisionMove.php and revisio...
authorTobias <churchofemacs@users.mediawiki.org>
Sun, 30 May 2010 18:00:01 +0000 (18:00 +0000)
committerTobias <churchofemacs@users.mediawiki.org>
Sun, 30 May 2010 18:00:01 +0000 (18:00 +0000)
includes/Article.php
includes/AutoLoader.php
includes/DefaultSettings.php
includes/HistoryPage.php
includes/LogPage.php
includes/SpecialPage.php
includes/Wiki.php
includes/specials/SpecialRevisionMove.php [new file with mode: 0644]
languages/messages/MessagesEn.php
maintenance/language/messages.inc

index 5b56bd4..597f6e3 100644 (file)
@@ -1790,10 +1790,12 @@ class Article {
         *                      on.
         * @param $lastRevIsRedirect Boolean: if given, will optimize adding and
         *                           removing rows in redirect table.
+        * @param $setNewFlag Boolean: Set to true if a page flag should be set
+        *                    Needed when $lastRevision has to be set to sth. !=0
         * @return bool true on success, false on failure
         * @private
         */
-       public function updateRevisionOn( &$dbw, $revision, $lastRevision = null, $lastRevIsRedirect = null ) {
+       public function updateRevisionOn( &$dbw, $revision, $lastRevision = null, $lastRevIsRedirect = null, $setNewFlag = false ) {
                wfProfileIn( __METHOD__ );
 
                $text = $revision->getText();
@@ -1806,11 +1808,15 @@ class Article {
                        $conditions['page_latest'] = $lastRevision;
                }
 
+               if ( !$setNewFlag ) {
+                       $setNewFlag = ( $lastRevision === 0 );
+               }
+
                $dbw->update( 'page',
                        array( /* SET */
                                'page_latest'      => $revision->getId(),
                                'page_touched'     => $dbw->timestamp(),
-                               'page_is_new'      => ( $lastRevision === 0 ) ? 1 : 0,
+                               'page_is_new'      => $setNewFlag,
                                'page_is_redirect' => $rt !== null ? 1 : 0,
                                'page_len'         => strlen( $text ),
                        ),
index ef49db2..ac03524 100644 (file)
@@ -571,6 +571,7 @@ $wgAutoloadLocalClasses = array(
        'RevDel_ArchivedFileItem' => 'includes/RevisionDelete.php',
        'RevDel_LogList' => 'includes/RevisionDelete.php',
        'RevDel_LogItem' => 'includes/RevisionDelete.php',
+       'SpecialRevisionMove' => 'includes/specials/SpecialRevisionMove.php',
        'ShortPagesPage' => 'includes/specials/SpecialShortpages.php',
        'SpecialActiveUsers' => 'includes/specials/SpecialActiveusers.php',
        'SpecialAllpages' => 'includes/specials/SpecialAllpages.php',
index 611cc6e..ca40b40 100644 (file)
@@ -3326,6 +3326,7 @@ $wgLogActions = array(
        'upload/revert'     => 'uploadedimage',
        'move/move'         => '1movedto2',
        'move/move_redir'   => '1movedto2_redir',
+       'move/move_rev'     => 'moverevlogentry',
        'import/upload'     => 'import-logentry-upload',
        'import/interwiki'  => 'import-logentry-interwiki',
        'merge/merge'       => 'pagemerge-logentry',
index a62d9a1..a9e13b4 100644 (file)
@@ -395,6 +395,20 @@ class HistoryPager extends ReverseChronologicalPager {
                                wfMsg( 'showhideselectedversions' )
                        ) . "\n";
                }
+               if( $wgUser->isAllowed( 'revisionmove' ) ) {
+                       $float = $wgContLang->alignEnd();
+                       # Note bug #20966, <button> is non-standard in IE<8
+                       $this->buttons .= Xml::element( 'button',
+                               array(
+                                       'type' => 'submit',
+                                       'name' => 'revisionmove',
+                                       'value' => '1',
+                                       'style' => "float: $float;",
+                                       'class' => 'mw-history-revisionmove-button',
+                               ),
+                               wfMsg( 'revisionmoveselectedversions' )
+                       ) . "\n";
+               }
                $this->buttons .= $this->submitButton( wfMsg( 'compareselectedversions'),
                        array(
                                'class'     => 'historysubmit',
@@ -486,10 +500,11 @@ class HistoryPager extends ReverseChronologicalPager {
                $classes = array();
 
                $del = '';
-               // User can delete revisions...
-               if( $wgUser->isAllowed( 'deleterevision' ) ) {
+               // Show checkboxes for each revision
+               if( $wgUser->isAllowed( 'deleterevision' ) || $wgUser->isAllowed( 'revisionmove' ) ) {
                        // If revision was hidden from sysops, disable the checkbox
-                       if( !$rev->userCan( Revision::DELETED_RESTRICTED ) ) {
+                       // However, if the user has revisionmove rights, we cannot disable the checkbox
+                       if( !$rev->userCan( Revision::DELETED_RESTRICTED ) && !$wgUser->isAllowed( 'revisionmove' ) ) {
                                $del = Xml::check( 'deleterevisions', false, array( 'disabled' => 'disabled' ) );
                        // Otherwise, enable the checkbox...
                        } else {
index 6377a2a..be5c08f 100644 (file)
@@ -253,7 +253,7 @@ class LogPage {
                                                }
 
                                        // Page moves
-                                       } else if ( $type == 'move' && count( $params ) == 3 ) {
+                                       } else if ( $type == 'move' && count( $params ) == 3 && $action != 'move_rev' ) {
                                                if( $params[2] ) {
                                                        if ( $skin ) {
                                                                $details .= ' [' . wfMsg( 'move-redirect-suppressed' ) . ']';
index dfe3506..b0f8fc4 100644 (file)
@@ -189,6 +189,7 @@ class SpecialPage {
                'Mypage'                    => array( 'SpecialMypage' ),
                'Mytalk'                    => array( 'SpecialMytalk' ),
                'Revisiondelete'            => 'SpecialRevisionDelete',
+               'RevisionMove'              => 'SpecialRevisionMove',
                'Specialpages'              => array( 'UnlistedSpecialPage', 'Specialpages' ),
                'Userlogout'                => array( 'UnlistedSpecialPage', 'Userlogout' ),
        );
index fa7a84c..cb0ef8b 100644 (file)
@@ -488,6 +488,8 @@ class MediaWiki {
                if ( $action === 'historysubmit' ) {
                        if ( $request->getBool( 'revisiondelete' ) ) {
                                $action = 'revisiondelete';
+                       } elseif ( $request->getBool( 'revisionmove' ) ) {
+                               $action = 'revisionmove';
                        } else {
                                $action = 'view';
                        }
@@ -576,6 +578,11 @@ class MediaWiki {
                                $special = SpecialPage::getPage( 'Revisiondelete' );
                                $special->execute( '' );
                                break;
+                       case 'revisionmove':
+                               # For revision move submission from history page
+                               $special = SpecialPage::getPage( 'RevisionMove' );
+                               $special->execute( '' );
+                               break;
                        default:
                                if( wfRunHooks( 'UnknownAction', array( $action, $article ) ) ) {
                                        $output->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
diff --git a/includes/specials/SpecialRevisionMove.php b/includes/specials/SpecialRevisionMove.php
new file mode 100644 (file)
index 0000000..ab6d1a1
--- /dev/null
@@ -0,0 +1,390 @@
+<?php
+/**
+ * Special page allowing users with the appropriate permissions to
+ * move revisions of a page to a new target (either an existing page or not)
+ * 
+ * The user selects revisions in the page history (HistoryPage.php),
+ * clicks on the submit button and gets this special page.
+ * A form is shown (showForm()) where the user has to enter a target page
+ * name and confirm the action with a post request & edit token.
+ * Then submit() is called, which does some checks and calls moveRevisions().
+ * If the target doesn't exist, a new page gets created. rev_page of the
+ * selected revisions is updated, after that it is determined whether page_latest
+ * of the target page and the source page require an update.
+ * 
+ * **** NOTE: This feature is EXPERIMENTAL.  ****
+ * **** Do not use on any productive system. ****
+ * 
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/* TODO In case page_deleted gets introduced some day, use it.
+ *      Currently it is possible with RevisionMove to make the latest revision
+ *      of a page a RevisionDeleted one. When that happens, the user is presented
+ *      an empty page with no error message whatsoever (in case he is not permitted
+ *      to view deleted edits).
+*/
+
+class SpecialRevisionMove extends UnlistedSpecialPage {
+
+       # common objects
+       var $mOldTitle; # Title object.
+       var $mNewTitle; # Title object. Desired new title
+       var $request; # WebRequest object, $wgRequest by default
+       var $skin; # Skin object
+       var $user; # User object
+
+       # variables
+       var $mIds; # Array of Ids to look at
+       var $mRevlist; # RevDel_RevisionList object - borrowed from RevisionDelete
+       var $mReason; # User-supplied reason for performing the move operation
+       var $mSubmit; # Boolean: Is this a submitted request?
+       var $mIsAllowedRevisionMove = false;
+
+       public function __construct( $name = 'RevisionMove' ) {
+               parent::__construct( $name );
+       }
+
+       /**
+        * @param $par subpage part, standard special page parameter, is ignored here
+        * @param $request optional WebRequest object. If it isn't set, $this->request 
+        *                 will be set to $wgRequest
+        * 
+        * Mostly initializes variables and calls either showForm() or submit()
+        */
+       public function execute( $par = '', $request = null ) {
+               global $wgUser, $wgOut, $wgSkin;
+
+               $this->setHeaders();
+               $this->outputHeader();
+
+               $this->mIsAllowedRevisionMove = $wgUser->isAllowed( 'revisionmove' );
+               $this->user = $wgUser;
+               $this->skin = $wgUser->getSkin();
+               if ( !$request instanceof WebRequest ) {
+                       $this->request = $GLOBALS['wgRequest'];
+               } else {
+                       $this->request = $request;
+               }
+
+               # Get correct title
+               if ( $this->request->getVal( 'action' ) == 'historysubmit' ) {
+                       $this->mOldTitle = Title::newFromText( $this->request->getVal( 'title' ) );
+               } else {
+                       $this->mOldTitle = Title::newFromText( $this->request->getVal( 'oldTitle' ) );
+               }
+
+               if ( !$this->mOldTitle instanceof Title ) {
+                       $wgOut->showErrorPage( 'revmove-badparam-title', 'revmove-badparam' );
+                       return;
+               }
+
+               $wgOut->setPagetitle( wfMsg( 'revisionmove', $this->mOldTitle->getPrefixedText() ) );
+               $oldTitleLink = $this->skin->link( $this->mOldTitle );
+               $wgOut->setSubtitle( wfMsg( 'revisionmove-backlink', $oldTitleLink ) );
+
+               $this->mReason = $this->request->getText( 'wpReason' );
+
+               # TODO maybe not needed here? Copied from SpecialRevisiondelete.php.
+               #      Keep for now, allow different inputs
+               # Handle our many different possible input types for ids
+               $ids = $this->request->getVal( 'ids' );
+               if ( !is_null( $ids ) ) {
+                       # Allow CSV, for backwards compatibility, or a single ID for show/hide links
+                       $this->mIds = explode( ',', $ids );
+               } else {
+                       # Array input
+                       $this->mIds = array_keys( $this->request->getArray( 'ids', array() ) );
+               }
+               $this->mIds = array_unique( array_filter( $this->mIds ) );
+
+               if ( is_null ( $this->mIds ) ) {
+                       $wgOut->showErrorPage( 'revmove-badparam-title', 'revmove-badparam' );
+                       return;
+               }
+               $this->mRevlist = new RevDel_RevisionList( $this, $this->mOldTitle, $this->mIds );
+
+               # Decide what to do: Show the form, or submit the changes
+               if ( $this->request->wasPosted() ) {
+                       $this->submit();
+               } else {
+                       $this->showForm();
+               }
+
+       }
+
+       /**
+        * Show a list of items that we will operate on and a field for the target name
+        */
+       public function showForm() {
+               global $wgOut, $wgUser;
+
+               if ( !$this->mIsAllowedRevisionMove ) {
+                       $permErrors = $this->mOldTitle->getUserPermissionsErrors( 'revisionmove', $this->user );
+                       $wgOut->showPermissionsErrorPage( $permErrors, 'revisionmove' );
+                       return false;
+               }
+
+               $wgOut->addWikiMsg( 'revmove-explain', $this->mOldTitle->getPrefixedText() );
+               $listNotEmpty = $this->listItems();
+               if ( !$listNotEmpty ) {
+                       return; # we're done, we already displayed an error earlier
+               }
+
+               $out = Xml::openElement( 'form', array( 'method' => 'post',
+                               'action' => $this->getTitle()->getLocalUrl( array( 'action' => 'submit' ) ), 
+                               'id' => 'mw-revmove-form' ) ) .
+                       Xml::fieldset( wfMsg( 'revmove-legend' ) ) .
+                       Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
+                       Xml::hidden( 'oldTitle', $this->mOldTitle->getPrefixedText() ) .
+                       '<div>' . Xml::inputLabel( wfMsg( 'revmove-reasonfield' ), 'wpReason', 'revmove-reasonfield', 60 ) . '</div>' .
+                       Xml::inputLabel( wfMsg( 'revmove-titlefield' ), 'newTitle', 'revmove-titlefield', 20, $this->mOldTitle->getPrefixedText() ) .
+                       Xml::hidden( 'ids', implode( ',', $this->mIds ) ) . 
+                       Xml::submitButton( wfMsg( 'revmove-submit' ),
+                                                       array( 'name' => 'wpSubmit' ) ) .
+                       Xml::closeElement( 'fieldset' ) . "\n" .
+                       Xml::closeElement( 'form' ) . "\n";
+               $wgOut->addHTML( $out );
+       }
+
+       /**
+        * Show a list of selected revisions and check the input
+        */
+       protected function listItems() {
+               global $wgOut;
+
+               $wgOut->addHTML( "<ul>" );
+
+               $numRevisions = 0;
+
+               # No revisions specified at all
+               if ( $this->mIds == array() ) {
+                       $wgOut->showErrorPage( 'revmove-norevisions-title', 'revmove-norevisions' );
+                       return false;
+               }
+
+               for ( $this->mRevlist->reset(); $this->mRevlist->current(); $this->mRevlist->next() ) {
+                       $item = $this->mRevlist->current();
+                       $numRevisions++;
+                       $wgOut->addHTML( $item->getHTML() );
+               }
+
+               # No valid revisions specified (e.g. only revs belonging to another page)
+               if( $numRevisions == 0 ) {
+                       $wgOut->showErrorPage( 'revmove-norevisions-title', 'revmove-norevisions' );
+                       return false;
+               }
+               
+               $wgOut->addHTML( "</ul>" );
+               return true;
+       }
+
+       /**
+        * Submit the posted changes (in $this->request).
+        * 
+        * This function does some checks and then calls moveRevisions(), which does the real work
+        */
+       public function submit() {
+               global $wgUser, $wgOut;
+
+               # Confirm permissions
+               if ( !$this->mIsAllowedRevisionMove ) {
+                       $permErrors = $this->mOldTitle->getUserPermissionsErrors( 'revisionmove', $this->user );
+                       $wgOut->showPermissionsErrorPage( $permErrors, 'revisionmove' );
+                       return false;
+               }
+               # Confirm Token
+               if( !$wgUser->matchEditToken( $this->request->getVal( 'wpEditToken' ) ) ) {
+                       $wgOut->showErrorPage( 'sessionfailure-title', 'sessionfailure' );
+                       return false;
+               }
+
+               $this->mNewTitle = Title::newFromText( $this->request->getVal( 'newTitle' ) );
+               if ( !$this->mNewTitle instanceof Title ) {
+                       $wgOut->showErrorPage( 'badtitle', 'badtitletext' );
+                       return false;
+               }
+
+               if ( $this->mNewTitle->getPrefixedText() == $this->mOldTitle->getPrefixedText() ) {
+                       $pagename = array( $this->mOldTitle->getPrefixedText() );
+                       $wgOut->showErrorPage( 'revmove-nullmove-title', 'revmove-nullmove', $pagename );
+                       return;
+               }
+
+               $this->moveRevisions();
+       }
+
+       /**
+        * This function actually move the revision. NEVER call this function, call submit()
+        */
+       protected function moveRevisions() {
+               global $wgOut;
+
+               $oldArticle = new Article( $this->mOldTitle );
+               $newArticle = new Article( $this->mNewTitle );
+
+               $idstring = implode( ", ", $this->mIds );
+
+               # Get DB connection and begin transaction
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->begin();
+
+               # Check if the target exists. If not, try creating it
+               if ( !$this->mNewTitle->exists() ) {
+                       $newArticle->insertOn( $dbw );
+                       $this->createArticle = true;
+               } else {
+                       $this->createArticle = false;
+               }
+
+               # This is where the magic happens:
+               # Update revision table
+               $dbw->update( 'revision',
+                       array( 'rev_page' => $this->mNewTitle->getArticleID() ),
+                       array( 
+                               'rev_id IN (' . $idstring . ')',
+                               'rev_page' => $this->mOldTitle->getArticleID(),
+                       ),
+                       __METHOD__
+               );
+               $modifiedRevsNum = $dbw->affectedRows();
+
+               # Check if we need to update page_latest
+               # Get the latest version of the revisions we are moving
+               $timestampNewPage = $this->queryLatestTimestamp( 
+                       $dbw,
+                       $this->mNewTitle->getArticleID(),
+                       array( 'rev_id IN (' . $idstring . ')' )
+               );
+
+               # Compare the new page's page_latest against db query.
+               # If we create a new page, we have to update anyway
+
+               $currentNewPageRev = Revision::newFromId( $this->mNewTitle->getLatestRevID() );
+               if ( $this->createArticle || $timestampNewPage > $currentNewPageRev->getTimestamp() ) {
+                       # we have to set page_latest to $timestampNewPage's revid
+                       $this->updatePageLatest( 
+                               $dbw, 
+                               $this->mNewTitle,
+                               $newArticle,
+                               $timestampNewPage, 
+                               array( 'rev_id IN (' . $idstring . ')' )
+                       );
+               }
+
+               # Update the old page's page_latest field
+               $timestampOldPage = $this->queryLatestTimestamp( 
+                       $dbw,
+                       $this->mOldTitle->getArticleID()
+               );
+
+               # If the timestamp is null that means the page doesn't have
+               # any revisions associated and should be deleted. In other words,
+               # someone misused revisionmove for the normal move function.
+               if ( is_null( $timestampOldPage ) ) {
+                       $dbw->delete(
+                               'page',
+                               array( 'page_id = ' . $this->mOldTitle->getArticleID() ),
+                               __METHOD__
+                       );
+                       $deletedOldPage = true;
+               } else {
+                       # page_latest has to be updated
+                       $currentOldPageRev = Revision::newFromId( $this->mOldTitle->getLatestRevID() );
+                       if ( $timestampOldPage < $currentOldPageRev->getTimestamp() ) {
+                               $this->updatePageLatest( 
+                                       $dbw, 
+                                       $this->mOldTitle,
+                                       $oldArticle,
+                                       $timestampOldPage
+                               );
+                       }
+                       # Purge the old one only if it hasn't been deleted
+                       $oldArticle->doPurge();
+               }
+
+               # All done, commit
+               $dbw->commit();
+
+               $this->logMove( $modifiedRevsNum );
+
+               # Purge new article
+               $newArticle->doPurge();
+
+               # If noting went wrong (i.e. returned), we are successful
+               $this->showSuccess( $modifiedRevsNum );
+       }
+
+       /**
+        * Query for the latest timestamp in order to update page_latest and
+        * page_timestamp.
+        * @param &$dbw Database object (Master)
+        * @param $articleId Integer page_id
+        * @param $conds array database conditions
+        * 
+        * @return String timestamp
+        */
+       protected function queryLatestTimestamp( &$dbw, $articleId, $conds = array() ) {
+               $timestampNewRow = $dbw->selectRow( 
+                       'revision', 
+                       'max(rev_timestamp) as maxtime',
+                       array_merge( array( 'rev_page' => $articleId ), $conds ),
+                       __METHOD__
+               );
+               return $timestampNewRow->maxtime;
+       }
+
+       /**
+        * Updates page_latest and similar database fields (see Article::updateRevisionOn).
+        * Called two times, for the new and the old page
+        * 
+        * @param &$dbw Database object (Master)
+        * @param $articleTitle Title object of the page
+        * @param $articleObj Article object of the page
+        * @param $timestamp to search for (use queryLatestTimestamp to get the latest)
+        * @param $conds array database conditions
+        * 
+        * @return boolean indicating success
+        */
+       protected function updatePageLatest( &$dbw, $articleTitle, &$articleObj, $timestamp, $conds = array() ) {
+               # Query to find out the rev_id
+               $revisionRow = $dbw->selectRow( 
+                       'revision', 
+                       'rev_id',
+                       array_merge( array(
+                               'rev_timestamp' => $timestamp,
+                               'rev_page' => $articleTitle->getArticleID(),
+                       ), $conds ),
+                       __METHOD__
+               );
+               # Update page_latest
+               $latestRev = Revision::newFromId( $revisionRow->rev_id );
+               return $articleObj->updateRevisionOn( $dbw, $latestRev, $articleTitle->getLatestRevID(), null, /* set new page flag */  true );
+       }
+
+       /**
+        * Add a log entry for the revision move
+       */
+       protected function logMove( $modifiedRevsNum ) {
+               $paramArray = array(
+                       $this->mNewTitle->getPrefixedText(),
+                       $modifiedRevsNum
+               );
+               $log = new LogPage( 'move' );
+               $log->addEntry( 'move_rev', $this->mOldTitle, $this->mReason, $paramArray, $this->user );
+
+       }
+
+       protected function showSuccess( $modifiedRevsNum ) {
+               global $wgOut;
+
+               if ( $this->createArticle ) {
+                       $wgOut->addWikiMsg( 'revmove-success-created', $modifiedRevsNum, 
+                               $this->mOldTitle->getPrefixedText(), $this->mNewTitle->getPrefixedText() );
+               } else {
+                       $wgOut->addWikiMsg( 'revmove-success-existing', $modifiedRevsNum, 
+                               $this->mOldTitle->getPrefixedText(), $this->mNewTitle->getPrefixedText() );
+               }
+       }
+}
index c21282b..73363cb 100644 (file)
@@ -1553,6 +1553,27 @@ Please check the logs.',
 'suppressionlogtext' => 'Below is a list of deletions and blocks involving content hidden from administrators.
 See the [[Special:IPBlockList|IP block list]] for the list of currently operational bans and blocks.',
 
+# SpecialRevisionMove // FIXME add to messages.inc
+'moverevlogentry'                  => 'moved $3 revisions from $1 to $2',
+'revisionmove'                     => 'Move revisions from "$1"',
+'revisionmove-backlink'            => '← $1',
+'revmove-explain'                  => 'The following revisions will be moved from $1 to the specified target page. If the target doesn\'t exist, it is created. Otherwise, these revisions will be merged into the page history.',
+'revmove-legend'                   => 'Set target page and summary',
+'revmove-submit'                   => 'Move revisions to selected page',
+'revisionmoveselectedversions'     => 'Move selected revisions',
+'revmove-reasonfield'              => 'Summary and reason:',
+'revmove-titlefield'               => 'Target page:',
+'revmove-badparam-title'           => 'Bad parameters',
+'revmove-badparam'                 => '<span class="error">Your request contains illegal or insufficient parameters. Please hit "back" and try again.</span>',
+'revmove-norevisions-title'        => 'Invalid target revision',
+'revmove-norevisions'              => 'You have either not specified a target revision(s) to perform this function or the specified revision does not exist.',
+'revmove-nullmove-title'           => 'Bad title',
+'revmove-nullmove'                 => '<span class="error">Source and target page are identical. Please hit "back" and enter a page name different to "$1".</span>',
+'revmove-permissionerror-title'    => 'Blub',
+'revmove-permissionerror'          => 'Bla',
+'revmove-success-existing'         => '{{PLURAL:$1|One revision from [[$2]] has|$1 revisions from [[$2]] have}} been moved to the existing page [[$3]].',
+'revmove-success-created'          => '{{PLURAL:$1|One revision from [[$2]] has|$1 revisions from [[$2]] have}} been moved to the newly created page [[$3]].',
+
 # History merging
 'mergehistory'                     => 'Merge page histories',
 'mergehistory-header'              => 'This page lets you merge revisions of the history of one source page into a newer page.
@@ -1955,6 +1976,7 @@ You can also choose to let others contact you through your user or talk page wit
 'action-userrights'           => 'edit all user rights',
 'action-userrights-interwiki' => 'edit user rights of users on other wikis',
 'action-siteadmin'            => 'lock or unlock the database',
+'action-revisionmove'         => 'move revisions',
 
 # Recent changes
 'nchanges'                          => '$1 {{PLURAL:$1|change|changes}}',
@@ -2739,7 +2761,10 @@ The last edit to the page was by [[User:$3|$3]] ([[User talk:$3|talk]]{{int:pipe
 'revertpage-nouser' => 'Reverted edits by (username removed) to last revision by [[User:$1|$1]]',
 'rollback-success'  => 'Reverted edits by $1;
 changed back to last revision by $2.',
-'sessionfailure'    => 'There seems to be a problem with your login session;
+
+# Edit tokens
+'sessionfailure-title' => 'Session failure',
+'sessionfailure'       => 'There seems to be a problem with your login session;
 this action has been canceled as a precaution against session hijacking.
 Please hit "back" and reload the page you came from, then try again.',
 
index 893bae1..09bbb4b 100644 (file)
@@ -748,6 +748,24 @@ $wgMessageStructure = array(
                'suppressionlog',
                'suppressionlogtext',
        ),
+       'revisionmove' => array(
+               'moverevlogentry',
+               'revisionmove',
+               'revmove-explain',
+               'revmove-legend',
+               'revmove-submit',
+               'revisionmoveselectedversions',
+               'revmove-reasonfield',
+               'revmove-titlefield',
+               'revmove-badparam-title' ,
+               'revmove-badparam',
+               'revmove-norevisions-title',
+               'revmove-norevisions',
+               'revmove-nullmove-title',
+               'revmove-nullmove',
+               'revmove-success-existing',
+               'revmove-success-created',
+       ),
        'mergehistory' => array(
                'mergehistory',
                'mergehistory-header',