| Index: trunk/phase3/includes/SpecialLog.php |
| — | — | @@ -405,8 +405,13 @@ |
| 406 | 406 | } |
| 407 | 407 | # Suppress $comment from old entries, not needed and can contain incorrect links |
| 408 | 408 | $comment = ''; |
| | 409 | + // Show unmerge link |
| | 410 | + } elseif ( $s->log_action == 'merge' ) { |
| | 411 | + $merge = SpecialPage::getTitleFor( 'Mergehistory' ); |
| | 412 | + $revert = '(' . $this->skin->makeKnownLinkObj( $merge, wfMsg('revertmerge'), |
| | 413 | + wfArrayToCGI( array('target' => $paramArray[0], 'dest' => $title->getPrefixedText() ) ) ) . ')'; |
| 409 | 414 | } elseif ( wfRunHooks( 'LogLine', array( $s->log_type, $s->log_action, $title, $paramArray, &$comment, &$revert ) ) ) { |
| 410 | | - //wfDebug( "Invoked LogLine hook for " $s->log_type . ", " . $s->log_action . "\n" ); |
| | 415 | + // wfDebug( "Invoked LogLine hook for " $s->log_type . ", " . $s->log_action . "\n" ); |
| 411 | 416 | // Do nothing. The implementation is handled by the hook modifiying the passed-by-ref parameters. |
| 412 | 417 | } |
| 413 | 418 | } |
| Index: trunk/phase3/includes/DefaultSettings.php |
| — | — | @@ -1099,6 +1099,7 @@ |
| 1100 | 1100 | $wgGroupPermissions['sysop']['blockemail'] = true; |
| 1101 | 1101 | $wgGroupPermissions['sysop']['markbotedits'] = true; |
| 1102 | 1102 | $wgGroupPermissions['sysop']['suppressredirect'] = true; |
| | 1103 | +#$wgGroupPermissions['sysop']['mergehistory'] = true; |
| 1103 | 1104 | |
| 1104 | 1105 | // Permission to change users' group assignments |
| 1105 | 1106 | $wgGroupPermissions['bureaucrat']['userrights'] = true; |
| — | — | @@ -2276,6 +2277,7 @@ |
| 2277 | 2278 | 'move', |
| 2278 | 2279 | 'import', |
| 2279 | 2280 | 'patrol', |
| | 2281 | + 'merge', |
| 2280 | 2282 | ); |
| 2281 | 2283 | |
| 2282 | 2284 | /** |
| — | — | @@ -2294,6 +2296,7 @@ |
| 2295 | 2297 | 'move' => 'movelogpage', |
| 2296 | 2298 | 'import' => 'importlogpage', |
| 2297 | 2299 | 'patrol' => 'patrol-log-page', |
| | 2300 | + 'merge' => 'mergelog', |
| 2298 | 2301 | ); |
| 2299 | 2302 | |
| 2300 | 2303 | /** |
| — | — | @@ -2312,6 +2315,7 @@ |
| 2313 | 2316 | 'move' => 'movelogpagetext', |
| 2314 | 2317 | 'import' => 'importlogpagetext', |
| 2315 | 2318 | 'patrol' => 'patrol-log-header', |
| | 2319 | + 'merge' => 'mergelogpagetext', |
| 2316 | 2320 | ); |
| 2317 | 2321 | |
| 2318 | 2322 | /** |
| — | — | @@ -2337,6 +2341,7 @@ |
| 2338 | 2342 | 'move/move_redir' => '1movedto2_redir', |
| 2339 | 2343 | 'import/upload' => 'import-logentry-upload', |
| 2340 | 2344 | 'import/interwiki' => 'import-logentry-interwiki', |
| | 2345 | + 'merge/merge' => 'pagemerge-logentry', |
| 2341 | 2346 | ); |
| 2342 | 2347 | |
| 2343 | 2348 | /** |
| Index: trunk/phase3/includes/SpecialPage.php |
| — | — | @@ -131,14 +131,14 @@ |
| 132 | 132 | 'Log' => array( 'SpecialPage', 'Log' ), |
| 133 | 133 | 'Blockip' => array( 'SpecialPage', 'Blockip', 'block' ), |
| 134 | 134 | 'Undelete' => array( 'SpecialPage', 'Undelete', 'deletedhistory' ), |
| 135 | | - 'Import' => array( 'SpecialPage', "Import", 'import' ), |
| | 135 | + 'Import' => array( 'SpecialPage', 'Import', 'import' ), |
| 136 | 136 | 'Lockdb' => array( 'SpecialPage', 'Lockdb', 'siteadmin' ), |
| 137 | 137 | 'Unlockdb' => array( 'SpecialPage', 'Unlockdb', 'siteadmin' ), |
| 138 | 138 | 'Userrights' => array( 'SpecialPage', 'Userrights', 'userrights' ), |
| 139 | 139 | 'MIMEsearch' => array( 'SpecialPage', 'MIMEsearch' ), |
| 140 | 140 | 'Unwatchedpages' => array( 'SpecialPage', 'Unwatchedpages', 'unwatchedpages' ), |
| 141 | 141 | 'Listredirects' => array( 'SpecialPage', 'Listredirects' ), |
| 142 | | - 'Revisiondelete' => array( 'SpecialPage', 'Revisiondelete', 'deleterevision' ), |
| | 142 | + 'Revisiondelete' => array( 'UnlistedSpecialPage', 'Revisiondelete', 'deleterevision' ), |
| 143 | 143 | 'Unusedtemplates' => array( 'SpecialPage', 'Unusedtemplates' ), |
| 144 | 144 | 'Randomredirect' => array( 'SpecialPage', 'Randomredirect' ), |
| 145 | 145 | 'Withoutinterwiki' => array( 'SpecialPage', 'Withoutinterwiki' ), |
| — | — | @@ -147,6 +147,7 @@ |
| 148 | 148 | 'Mytalk' => array( 'SpecialMytalk' ), |
| 149 | 149 | 'Mycontributions' => array( 'SpecialMycontributions' ), |
| 150 | 150 | 'Listadmins' => array( 'SpecialRedirectToSpecial', 'Listadmins', 'Listusers', 'sysop' ), |
| | 151 | + 'MergeHistory' => array( 'SpecialPage', 'MergeHistory', 'mergehistory' ), |
| 151 | 152 | 'Listbots' => array( 'SpecialRedirectToSpecial', 'Listbots', 'Listusers', 'bot' ), |
| 152 | 153 | ); |
| 153 | 154 | |
| Index: trunk/phase3/includes/LogPage.php |
| — | — | @@ -173,6 +173,11 @@ |
| 174 | 174 | $text = $wgContLang->ucfirst( $title->getText() ); |
| 175 | 175 | $titleLink = $skin->makeLinkObj( Title::makeTitle( NS_USER, $text ) ); |
| 176 | 176 | break; |
| | 177 | + case 'merge': |
| | 178 | + $titleLink = $skin->makeLinkObj( $title, $title->getPrefixedText(), 'redirect=no' ); |
| | 179 | + $params[0] = $skin->makeLinkObj( Title::newFromText( $params[0] ), htmlspecialchars( $params[0] ) ); |
| | 180 | + $params[1] = $wgLang->timeanddate( $params[1] ); |
| | 181 | + break; |
| 177 | 182 | default: |
| 178 | 183 | $titleLink = $skin->makeLinkObj( $title ); |
| 179 | 184 | } |
| Index: trunk/phase3/includes/SpecialMergeHistory.php |
| — | — | @@ -0,0 +1,388 @@ |
| | 2 | +<?php |
| | 3 | + |
| | 4 | +/** |
| | 5 | + * Special page allowing users with the appropriate permissions to |
| | 6 | + * merge article histories, with some restrictions |
| | 7 | + * |
| | 8 | + * @addtogroup SpecialPage |
| | 9 | + */ |
| | 10 | + |
| | 11 | +/** |
| | 12 | + * Constructor |
| | 13 | + */ |
| | 14 | +function wfSpecialMergehistory( $par ) { |
| | 15 | + global $wgRequest; |
| | 16 | + |
| | 17 | + $form = new MergehistoryForm( $wgRequest, $par ); |
| | 18 | + $form->execute(); |
| | 19 | +} |
| | 20 | + |
| | 21 | +/** |
| | 22 | + * The HTML form for Special:MergeHistory, which allows users with the appropriate |
| | 23 | + * permissions to view and restore deleted content. |
| | 24 | + * @addtogroup SpecialPage |
| | 25 | + */ |
| | 26 | +class MergehistoryForm { |
| | 27 | + var $mAction, $mTarget, $mDest, $mTimestamp, $mTargetID, $mDestID, $mComment; |
| | 28 | + var $mTargetObj, $mDestObj; |
| | 29 | + |
| | 30 | + function MergehistoryForm( $request, $par = "" ) { |
| | 31 | + global $wgUser; |
| | 32 | + |
| | 33 | + $this->mAction = $request->getVal( 'action' ); |
| | 34 | + $this->mTarget = $request->getVal( 'target' ); |
| | 35 | + $this->mDest = $request->getVal( 'dest' ); |
| | 36 | + |
| | 37 | + $this->mTargetID = intval( $request->getVal( 'targetID' ) ); |
| | 38 | + $this->mDestID = intval( $request->getVal( 'destID' ) ); |
| | 39 | + $this->mTimestamp = $request->getVal( 'mergepoint' ); |
| | 40 | + $this->mComment = $request->getText( 'wpComment' ); |
| | 41 | + |
| | 42 | + $this->mMerge = $request->wasPosted() && $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) ); |
| | 43 | + // target page |
| | 44 | + if( $this->mTarget !== "" ) { |
| | 45 | + $this->mTargetObj = Title::newFromURL( $this->mTarget ); |
| | 46 | + } else { |
| | 47 | + $this->mTargetObj = NULL; |
| | 48 | + } |
| | 49 | + # Destination |
| | 50 | + if( $this->mDest !== "" ) { |
| | 51 | + $this->mDestObj = Title::newFromURL( $this->mDest ); |
| | 52 | + } else { |
| | 53 | + $this->mDestObj = NULL; |
| | 54 | + } |
| | 55 | + |
| | 56 | + $this->preCacheMessages(); |
| | 57 | + } |
| | 58 | + |
| | 59 | + /** |
| | 60 | + * As we use the same small set of messages in various methods and that |
| | 61 | + * they are called often, we call them once and save them in $this->message |
| | 62 | + */ |
| | 63 | + function preCacheMessages() { |
| | 64 | + // Precache various messages |
| | 65 | + if( !isset( $this->message ) ) { |
| | 66 | + $this->message['last'] = wfMsgExt( 'last', array( 'escape') ); |
| | 67 | + } |
| | 68 | + } |
| | 69 | + |
| | 70 | + function execute() { |
| | 71 | + global $wgOut, $wgUser; |
| | 72 | + |
| | 73 | + $wgOut->setPagetitle( wfMsgHtml( "mergehistory" ) ); |
| | 74 | + |
| | 75 | + if( $this->mTargetID && $this->mDestID && $this->mAction=="submit" && $this->mMerge ) { |
| | 76 | + return $this->merge(); |
| | 77 | + } |
| | 78 | + |
| | 79 | + if( is_object($this->mTargetObj) && is_object($this->mDestObj) ) { |
| | 80 | + return $this->showHistory(); |
| | 81 | + } |
| | 82 | + |
| | 83 | + return $this->showMergeForm(); |
| | 84 | + } |
| | 85 | + |
| | 86 | + function showMergeForm() { |
| | 87 | + global $wgOut, $wgScript; |
| | 88 | + |
| | 89 | + $wgOut->addWikiText( wfMsg( 'mergehistory-header' ) ); |
| | 90 | + |
| | 91 | + $wgOut->addHtml( |
| | 92 | + Xml::openElement( 'form', array( |
| | 93 | + 'method' => 'get', |
| | 94 | + 'action' => $wgScript ) ) . |
| | 95 | + '<fieldset>' . |
| | 96 | + Xml::element( 'legend', array(), |
| | 97 | + wfMsg( 'mergehistory-box' ) ) . |
| | 98 | + Xml::hidden( 'title', |
| | 99 | + SpecialPage::getTitleFor( 'Mergehistory' )->getPrefixedDbKey() ) . |
| | 100 | + Xml::openElement( 'table' ) . |
| | 101 | + "<tr> |
| | 102 | + <td>".Xml::Label( wfMsg( 'mergehistory-from' ), 'target' )."</td> |
| | 103 | + <td>".Xml::input( 'target', 30, $this->mTarget, array('id'=>'target') )."</td> |
| | 104 | + </tr><tr> |
| | 105 | + <td>".Xml::Label( wfMsg( 'mergehistory-into' ), 'dest' )."</td> |
| | 106 | + <td>".Xml::input( 'dest', 30, $this->mDest, array('id'=>'dest') )."</td> |
| | 107 | + </tr><tr><td>" . |
| | 108 | + Xml::submitButton( wfMsg( 'mergehistory-go' ) ) . |
| | 109 | + "</td></tr>" . |
| | 110 | + Xml::closeElement( 'table' ) . |
| | 111 | + '</fieldset>' . |
| | 112 | + '</form>' ); |
| | 113 | + } |
| | 114 | + |
| | 115 | + private function showHistory() { |
| | 116 | + global $wgLang, $wgContLang, $wgUser, $wgOut; |
| | 117 | + |
| | 118 | + $this->sk = $wgUser->getSkin(); |
| | 119 | + |
| | 120 | + $wgOut->setPagetitle( wfMsg( "mergehistory" ) ); |
| | 121 | + |
| | 122 | + $this->showMergeForm(); |
| | 123 | + |
| | 124 | + # List all stored revisions |
| | 125 | + $revisions = new MergeHistoryPager( $this, array(), $this->mTargetObj, $this->mDestObj ); |
| | 126 | + $haveRevisions = $revisions && $revisions->getNumRows() > 0; |
| | 127 | + |
| | 128 | + $titleObj = SpecialPage::getTitleFor( "Mergehistory" ); |
| | 129 | + $action = $titleObj->getLocalURL( "action=submit" ); |
| | 130 | + # Start the form here |
| | 131 | + $top = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'merge' ) ); |
| | 132 | + $wgOut->addHtml( $top ); |
| | 133 | + |
| | 134 | + if( $haveRevisions ) { |
| | 135 | + # Format the user-visible controls (comment field, submission button) |
| | 136 | + # in a nice little table |
| | 137 | + $align = $wgContLang->isRtl() ? 'left' : 'right'; |
| | 138 | + $table = |
| | 139 | + Xml::openElement( 'fieldset' ) . |
| | 140 | + Xml::openElement( 'table' ) . |
| | 141 | + "<tr> |
| | 142 | + <td colspan='2'>" . |
| | 143 | + wfMsgExt( 'mergehistory-merge', array('parseinline'), |
| | 144 | + $this->mTargetObj->getPrefixedText(), $this->mDestObj->getPrefixedText() ) . |
| | 145 | + "</td> |
| | 146 | + </tr> |
| | 147 | + <tr> |
| | 148 | + <td align='$align'>" . |
| | 149 | + Xml::label( wfMsg( 'undeletecomment' ), 'wpComment' ) . |
| | 150 | + "</td> |
| | 151 | + <td>" . |
| | 152 | + Xml::input( 'wpComment', 50, $this->mComment ) . |
| | 153 | + "</td> |
| | 154 | + </tr> |
| | 155 | + <tr> |
| | 156 | + <td> </td> |
| | 157 | + <td>" . |
| | 158 | + Xml::submitButton( wfMsg( 'mergehistory-submit' ), array( 'name' => 'merge', 'id' => 'mw-merge-submit' ) ) . |
| | 159 | + "</td> |
| | 160 | + </tr>" . |
| | 161 | + Xml::closeElement( 'table' ) . |
| | 162 | + Xml::closeElement( 'fieldset' ); |
| | 163 | + |
| | 164 | + $wgOut->addHtml( $table ); |
| | 165 | + } |
| | 166 | + |
| | 167 | + $wgOut->addHTML( "<h2 id=\"mw-mergehistory\">" . wfMsgHtml( "mergehistory-list" ) . "</h2>\n" ); |
| | 168 | + |
| | 169 | + if( $haveRevisions ) { |
| | 170 | + $wgOut->addHTML( $revisions->getNavigationBar() ); |
| | 171 | + $wgOut->addHTML( "<ul>" ); |
| | 172 | + $wgOut->addHTML( $revisions->getBody() ); |
| | 173 | + $wgOut->addHTML( "</ul>" ); |
| | 174 | + $wgOut->addHTML( $revisions->getNavigationBar() ); |
| | 175 | + } else { |
| | 176 | + $wgOut->addWikiText( wfMsg( "mergehistory-empty" ) ); |
| | 177 | + } |
| | 178 | + |
| | 179 | + # Show relevant lines from the deletion log: |
| | 180 | + $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'merge' ) ) . "</h2>\n" ); |
| | 181 | + $logViewer = new LogViewer( |
| | 182 | + new LogReader( |
| | 183 | + new FauxRequest( |
| | 184 | + array( 'page' => $this->mTargetObj->getPrefixedText(), |
| | 185 | + 'type' => 'merge' ) ) ) ); |
| | 186 | + $logViewer->showList( $wgOut ); |
| | 187 | + |
| | 188 | + # Slip in the hidden controls here |
| | 189 | + # When we submit, go by page ID to avoid some nasty but unlikely collisions. |
| | 190 | + # Such would happen if a page was renamed after the form loaded, but before submit |
| | 191 | + $misc = Xml::hidden( 'targetID', $this->mTargetObj->getArticleID() ); |
| | 192 | + $misc .= Xml::hidden( 'destID', $this->mDestObj->getArticleID() ); |
| | 193 | + $misc .= Xml::hidden( 'target', $this->mTarget ); |
| | 194 | + $misc .= Xml::hidden( 'dest', $this->mDest ); |
| | 195 | + $misc .= Xml::hidden( 'wpEditToken', $wgUser->editToken() ); |
| | 196 | + $misc .= Xml::closeElement( 'form' ); |
| | 197 | + $wgOut->addHtml( $misc ); |
| | 198 | + |
| | 199 | + return true; |
| | 200 | + } |
| | 201 | + |
| | 202 | + function formatRevisionRow( $row ) { |
| | 203 | + global $wgUser, $wgLang; |
| | 204 | + |
| | 205 | + $rev = new Revision( $row ); |
| | 206 | + |
| | 207 | + $stxt = ''; |
| | 208 | + $last = $this->message['last']; |
| | 209 | + |
| | 210 | + $ts = wfTimestamp( TS_MW, $row->rev_timestamp ); |
| | 211 | + $checkBox = wfRadio( "mergepoint", $ts, false ); |
| | 212 | + |
| | 213 | + $pageLink = $this->sk->makeKnownLinkObj( $rev->getTitle(), |
| | 214 | + $wgLang->timeanddate( $ts ), 'oldid=' . $rev->getID() ); |
| | 215 | + if( $rev->isDeleted( Revision::DELETED_TEXT ) ) { |
| | 216 | + $pageLink = '<span class="history-deleted">' . $pageLink . '</span>'; |
| | 217 | + } |
| | 218 | + |
| | 219 | + # Last link |
| | 220 | + if( !$rev->userCan( Revision::DELETED_TEXT ) ) |
| | 221 | + $last = $this->message['last']; |
| | 222 | + else if( isset($this->prevId[$row->rev_id]) ) |
| | 223 | + $last = $this->sk->makeKnownLinkObj( $rev->getTitle(), $this->message['last'], |
| | 224 | + "&diff=" . $row->rev_id . "&oldid=" . $this->prevId[$row->rev_id] ); |
| | 225 | + |
| | 226 | + $userLink = $this->sk->revUserTools( $rev ); |
| | 227 | + |
| | 228 | + if(!is_null($size = $row->rev_len)) { |
| | 229 | + if($size == 0) |
| | 230 | + $stxt = wfMsgHtml('historyempty'); |
| | 231 | + else |
| | 232 | + $stxt = wfMsgHtml('historysize', $wgLang->formatNum( $size ) ); |
| | 233 | + } |
| | 234 | + $comment = $this->sk->revComment( $rev ); |
| | 235 | + |
| | 236 | + return "<li>$checkBox ($last) $pageLink . . $userLink $stxt $comment</li>"; |
| | 237 | + } |
| | 238 | + |
| | 239 | + /** |
| | 240 | + * Fetch revision text link if it's available to all users |
| | 241 | + * @return string |
| | 242 | + */ |
| | 243 | + function getPageLink( $row, $titleObj, $ts, $target ) { |
| | 244 | + global $wgLang; |
| | 245 | + |
| | 246 | + if( !$this->userCan($row, Revision::DELETED_TEXT) ) { |
| | 247 | + return '<span class="history-deleted">' . $wgLang->timeanddate( $ts, true ) . '</span>'; |
| | 248 | + } else { |
| | 249 | + $link = $this->sk->makeKnownLinkObj( $titleObj, |
| | 250 | + $wgLang->timeanddate( $ts, true ), "target=$target×tamp=$ts" ); |
| | 251 | + if( $this->isDeleted($row, Revision::DELETED_TEXT) ) |
| | 252 | + $link = '<span class="history-deleted">' . $link . '</span>'; |
| | 253 | + return $link; |
| | 254 | + } |
| | 255 | + } |
| | 256 | + |
| | 257 | + function merge() { |
| | 258 | + global $wgOut, $wgUser; |
| | 259 | + # Get the titles directly from the IDs, in case the target page params |
| | 260 | + # were spoofed. The queries are done based on the IDs, so it's best to |
| | 261 | + # keep it consistent... |
| | 262 | + $targetTitle = Title::newFromID( $this->mTargetID ); |
| | 263 | + $destTitle = Title::newFromID( $this->mDestID ); |
| | 264 | + if( is_null($targetTitle) || is_null($destTitle) ) |
| | 265 | + return false; // validate these |
| | 266 | + # Verify that this timestamp is valid |
| | 267 | + # Must be older than the destination page |
| | 268 | + $dbw = wfGetDB( DB_MASTER ); |
| | 269 | + $maxtimestamp = $dbw->selectField( 'revision', 'MIN(rev_timestamp)', |
| | 270 | + array('rev_page' => $this->mDestID ), |
| | 271 | + __METHOD__ ); |
| | 272 | + # Destination page must exist with revisions |
| | 273 | + if( !$maxtimestamp ) { |
| | 274 | + $wgOut->addWikiText( wfMsg('mergehistory-fail') ); |
| | 275 | + return false; |
| | 276 | + } |
| | 277 | + # Leave the latest version no matter what |
| | 278 | + $lasttime = $dbw->selectField( array('page','revision'), |
| | 279 | + 'rev_timestamp', |
| | 280 | + array('page_id' => $this->mTargetID, 'page_latest = rev_id' ), |
| | 281 | + __METHOD__ ); |
| | 282 | + # Take the most restrictive of the twain |
| | 283 | + $maxtimestamp = ($lasttime < $maxtimestamp) ? $lasttime : $maxtimestamp; |
| | 284 | + if( $this->mTimestamp >= $maxtimestamp ) { |
| | 285 | + $wgOut->addHtml( wfMsg('mergehistory-fail') ); |
| | 286 | + return false; |
| | 287 | + } |
| | 288 | + # Update the revisions |
| | 289 | + if( $this->mTimestamp ) |
| | 290 | + $timewhere = "rev_timestamp <= {$this->mTimestamp}"; |
| | 291 | + else |
| | 292 | + $timewhere = '1 = 1'; |
| | 293 | + |
| | 294 | + $dbw->update( 'revision', |
| | 295 | + array( 'rev_page' => $this->mDestID ), |
| | 296 | + array( 'rev_page' => $this->mTargetID, |
| | 297 | + "rev_timestamp < {$maxtimestamp}", |
| | 298 | + $timewhere ), |
| | 299 | + __METHOD__ ); |
| | 300 | + # Check if this did anything |
| | 301 | + $count = $dbw->affectedRows(); |
| | 302 | + if( !$count ) { |
| | 303 | + $wgOut->addHtml( wfMsg('mergehistory-fail') ); |
| | 304 | + return false; |
| | 305 | + } |
| | 306 | + # Update our logs |
| | 307 | + $log = new LogPage( 'merge' ); |
| | 308 | + $log->addEntry( 'merge', $targetTitle, $this->mComment, |
| | 309 | + array($destTitle->getPrefixedText(),$this->mTimestamp) ); |
| | 310 | + |
| | 311 | + $wgOut->addHtml( wfMsgExt( 'mergehistory-success', array('parseinline'), |
| | 312 | + $targetTitle->getPrefixedText(), $destTitle->getPrefixedText(), $count ) ); |
| | 313 | + |
| | 314 | + wfRunHooks( 'ArticleMergeComplete', array( $targetTitle, $destTitle ) ); |
| | 315 | + |
| | 316 | + return true; |
| | 317 | + } |
| | 318 | +} |
| | 319 | + |
| | 320 | +class MergeHistoryPager extends ReverseChronologicalPager { |
| | 321 | + public $mForm, $mConds; |
| | 322 | + |
| | 323 | + function __construct( $form, $conds = array(), $title, $title2 ) { |
| | 324 | + $this->mForm = $form; |
| | 325 | + $this->mConds = $conds; |
| | 326 | + $this->title = $title; |
| | 327 | + $this->articleID = $title->getArticleID(); |
| | 328 | + |
| | 329 | + $dbr = wfGetDB( DB_SLAVE ); |
| | 330 | + $maxtimestamp = $dbr->selectField( 'revision', 'MIN(rev_timestamp)', |
| | 331 | + array('rev_page' => $title2->getArticleID() ), |
| | 332 | + __METHOD__ ); |
| | 333 | + $this->maxTimestamp = $maxtimestamp; |
| | 334 | + |
| | 335 | + parent::__construct(); |
| | 336 | + } |
| | 337 | + |
| | 338 | + function getStartBody() { |
| | 339 | + wfProfileIn( __METHOD__ ); |
| | 340 | + # Do a link batch query |
| | 341 | + $this->mResult->seek( 0 ); |
| | 342 | + $batch = new LinkBatch(); |
| | 343 | + # Give some pointers to make (last) links |
| | 344 | + $this->mForm->prevId = array(); |
| | 345 | + while( $row = $this->mResult->fetchObject() ) { |
| | 346 | + $batch->addObj( Title::makeTitleSafe( NS_USER, $row->rev_user_text ) ); |
| | 347 | + $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->rev_user_text ) ); |
| | 348 | + |
| | 349 | + $rev_id = isset($rev_id) ? $rev_id : $row->rev_id; |
| | 350 | + if( $rev_id > $row->rev_id ) |
| | 351 | + $this->mForm->prevId[$rev_id] = $row->rev_id; |
| | 352 | + else if( $rev_id < $row->rev_id ) |
| | 353 | + $this->mForm->prevId[$row->rev_id] = $rev_id; |
| | 354 | + |
| | 355 | + $rev_id = $row->rev_id; |
| | 356 | + } |
| | 357 | + |
| | 358 | + $batch->execute(); |
| | 359 | + $this->mResult->seek( 0 ); |
| | 360 | + |
| | 361 | + wfProfileOut( __METHOD__ ); |
| | 362 | + return ''; |
| | 363 | + } |
| | 364 | + |
| | 365 | + function formatRow( $row ) { |
| | 366 | + $block = new Block; |
| | 367 | + return $this->mForm->formatRevisionRow( $row ); |
| | 368 | + } |
| | 369 | + |
| | 370 | + function getQueryInfo() { |
| | 371 | + $conds = $this->mConds; |
| | 372 | + $conds['rev_page'] = $this->articleID; |
| | 373 | + $conds[] = "rev_timestamp < {$this->maxTimestamp}"; |
| | 374 | + # Skip the latest one, as that could cause problems |
| | 375 | + if( $page = $this->title->getLatestRevID() ) |
| | 376 | + $conds[] = "rev_id != {$page}"; |
| | 377 | + |
| | 378 | + return array( |
| | 379 | + 'tables' => array('revision'), |
| | 380 | + 'fields' => array( 'rev_minor_edit', 'rev_timestamp', 'rev_user', 'rev_user_text', 'rev_comment', |
| | 381 | + 'rev_id', 'rev_page', 'rev_text_id', 'rev_len', 'rev_deleted' ), |
| | 382 | + 'conds' => $conds |
| | 383 | + ); |
| | 384 | + } |
| | 385 | + |
| | 386 | + function getIndexField() { |
| | 387 | + return 'rev_timestamp'; |
| | 388 | + } |
| | 389 | +} |
| Property changes on: trunk/phase3/includes/SpecialMergeHistory.php |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| 1 | 390 | + native |
| Index: trunk/phase3/languages/messages/MessagesEn.php |
| — | — | @@ -1183,6 +1183,30 @@ |
| 1184 | 1184 | 'overlogpagetext' => 'Below is a list of the most recent deletions and blocks involving content |
| 1185 | 1185 | hidden from Sysops. See the [[Special:Ipblocklist|IP block list]] for the list of currently operational bans and blocks.', |
| 1186 | 1186 | |
| | 1187 | +# History merging |
| | 1188 | +'mergehistory' => 'Merge page histories', |
| | 1189 | +'mergehistory-header' => "This page lets you merge revisions of the history of one source page into a newer page. |
| | 1190 | +Make sure that this change will maintain historical page continuity. |
| | 1191 | + |
| | 1192 | +'''At least the current revision of the source page must remain.'''", |
| | 1193 | +'mergehistory-box' => 'Merge revisions of two pages:', |
| | 1194 | +'mergehistory-from' => 'Source page:', |
| | 1195 | +'mergehistory-into' => 'Destination page:', |
| | 1196 | +'mergehistory-list' => 'Mergeable edit history', |
| | 1197 | +'mergehistory-merge' => 'The following revisions of [[:$1|$1]] can be merged into [[:$2|$2]]. Use the radio |
| | 1198 | +button column to merge in only the revisions created at and before the specified time. Note that using the |
| | 1199 | +navigation links will reset this column.', |
| | 1200 | +'mergehistory-go' => 'Show mergeable edits', |
| | 1201 | +'mergehistory-submit' => 'Merge revisions', |
| | 1202 | +'mergehistory-empty' => 'No revisions can be merged', |
| | 1203 | +'mergehistory-success' => '$3 revisions of [[:$1]] successfully merged into [[:$2]].', |
| | 1204 | +'mergehistory-fail' => 'Unable to perform history merge, please recheck the page and time parameters.', |
| | 1205 | + |
| | 1206 | +'mergelog' => 'Merge log', |
| | 1207 | +'pagemerge-logentry' => 'merged $1 into $2 (revisions up to $3)', |
| | 1208 | +'revertmerge' => 'Unmerge', |
| | 1209 | +'mergelogpagetext' => 'Below is a list of the most recent merges of one page history into another.', |
| | 1210 | + |
| 1187 | 1211 | # Diffs |
| 1188 | 1212 | 'history-title' => 'Revision history of "$1"', |
| 1189 | 1213 | 'difference' => '(Difference between revisions)', |