[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License along 15 * with this program; if not, write to the Free Software Foundation, Inc., 16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 * http://www.gnu.org/copyleft/gpl.html 18 * 19 * @file 20 */ 21 22 /** 23 * Handles the backend logic of moving a page from one title 24 * to another. 25 * 26 * @since 1.24 27 */ 28 class MovePage { 29 30 /** 31 * @var Title 32 */ 33 protected $oldTitle; 34 35 /** 36 * @var Title 37 */ 38 protected $newTitle; 39 40 public function __construct( Title $oldTitle, Title $newTitle ) { 41 $this->oldTitle = $oldTitle; 42 $this->newTitle = $newTitle; 43 } 44 45 /** 46 * @param User $user 47 * @param string $reason 48 * @param bool $createRedirect 49 * @return Status 50 */ 51 public function move( User $user, $reason, $createRedirect ) { 52 global $wgCategoryCollation; 53 54 // If it is a file, move it first. 55 // It is done before all other moving stuff is done because it's hard to revert. 56 $dbw = wfGetDB( DB_MASTER ); 57 if ( $this->oldTitle->getNamespace() == NS_FILE ) { 58 $file = wfLocalFile( $this->oldTitle ); 59 if ( $file->exists() ) { 60 $status = $file->move( $this->newTitle ); 61 if ( !$status->isOk() ) { 62 return $status; 63 } 64 } 65 // Clear RepoGroup process cache 66 RepoGroup::singleton()->clearCache( $this->oldTitle ); 67 RepoGroup::singleton()->clearCache( $this->newTitle ); # clear false negative cache 68 } 69 70 $dbw->begin( __METHOD__ ); # If $file was a LocalFile, its transaction would have closed our own. 71 $pageid = $this->oldTitle->getArticleID( Title::GAID_FOR_UPDATE ); 72 $protected = $this->oldTitle->isProtected(); 73 74 // Do the actual move 75 $this->moveToInternal( $user, $this->newTitle, $reason, $createRedirect ); 76 77 // Refresh the sortkey for this row. Be careful to avoid resetting 78 // cl_timestamp, which may disturb time-based lists on some sites. 79 // @todo This block should be killed, it's duplicating code 80 // from LinksUpdate::getCategoryInsertions() and friends. 81 $prefixes = $dbw->select( 82 'categorylinks', 83 array( 'cl_sortkey_prefix', 'cl_to' ), 84 array( 'cl_from' => $pageid ), 85 __METHOD__ 86 ); 87 if ( $this->newTitle->getNamespace() == NS_CATEGORY ) { 88 $type = 'subcat'; 89 } elseif ( $this->newTitle->getNamespace() == NS_FILE ) { 90 $type = 'file'; 91 } else { 92 $type = 'page'; 93 } 94 foreach ( $prefixes as $prefixRow ) { 95 $prefix = $prefixRow->cl_sortkey_prefix; 96 $catTo = $prefixRow->cl_to; 97 $dbw->update( 'categorylinks', 98 array( 99 'cl_sortkey' => Collation::singleton()->getSortKey( 100 $this->newTitle->getCategorySortkey( $prefix ) ), 101 'cl_collation' => $wgCategoryCollation, 102 'cl_type' => $type, 103 'cl_timestamp=cl_timestamp' ), 104 array( 105 'cl_from' => $pageid, 106 'cl_to' => $catTo ), 107 __METHOD__ 108 ); 109 } 110 111 $redirid = $this->oldTitle->getArticleID(); 112 113 if ( $protected ) { 114 # Protect the redirect title as the title used to be... 115 $dbw->insertSelect( 'page_restrictions', 'page_restrictions', 116 array( 117 'pr_page' => $redirid, 118 'pr_type' => 'pr_type', 119 'pr_level' => 'pr_level', 120 'pr_cascade' => 'pr_cascade', 121 'pr_user' => 'pr_user', 122 'pr_expiry' => 'pr_expiry' 123 ), 124 array( 'pr_page' => $pageid ), 125 __METHOD__, 126 array( 'IGNORE' ) 127 ); 128 # Update the protection log 129 $log = new LogPage( 'protect' ); 130 $comment = wfMessage( 131 'prot_1movedto2', 132 $this->oldTitle->getPrefixedText(), 133 $this->newTitle->getPrefixedText() 134 )->inContentLanguage()->text(); 135 if ( $reason ) { 136 $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason; 137 } 138 // @todo FIXME: $params? 139 $logId = $log->addEntry( 140 'move_prot', 141 $this->newTitle, 142 $comment, 143 array( $this->oldTitle->getPrefixedText() ), 144 $user 145 ); 146 147 // reread inserted pr_ids for log relation 148 $insertedPrIds = $dbw->select( 149 'page_restrictions', 150 'pr_id', 151 array( 'pr_page' => $redirid ), 152 __METHOD__ 153 ); 154 $logRelationsValues = array(); 155 foreach ( $insertedPrIds as $prid ) { 156 $logRelationsValues[] = $prid->pr_id; 157 } 158 $log->addRelations( 'pr_id', $logRelationsValues, $logId ); 159 } 160 161 // Update *_from_namespace fields as needed 162 if ( $this->oldTitle->getNamespace() != $this->newTitle->getNamespace() ) { 163 $dbw->update( 'pagelinks', 164 array( 'pl_from_namespace' => $this->newTitle->getNamespace() ), 165 array( 'pl_from' => $pageid ), 166 __METHOD__ 167 ); 168 $dbw->update( 'templatelinks', 169 array( 'tl_from_namespace' => $this->newTitle->getNamespace() ), 170 array( 'tl_from' => $pageid ), 171 __METHOD__ 172 ); 173 $dbw->update( 'imagelinks', 174 array( 'il_from_namespace' => $this->newTitle->getNamespace() ), 175 array( 'il_from' => $pageid ), 176 __METHOD__ 177 ); 178 } 179 180 # Update watchlists 181 $oldtitle = $this->oldTitle->getDBkey(); 182 $newtitle = $this->newTitle->getDBkey(); 183 $oldsnamespace = MWNamespace::getSubject( $this->oldTitle->getNamespace() ); 184 $newsnamespace = MWNamespace::getSubject( $this->newTitle->getNamespace() ); 185 if ( $oldsnamespace != $newsnamespace || $oldtitle != $newtitle ) { 186 WatchedItem::duplicateEntries( $this->oldTitle, $this->newTitle ); 187 } 188 189 $dbw->commit( __METHOD__ ); 190 191 wfRunHooks( 'TitleMoveComplete', array( &$this->oldTitle, &$this->newTitle, &$user, $pageid, $redirid, $reason ) ); 192 return Status::newGood(); 193 194 } 195 196 /** 197 * Move page to a title which is either a redirect to the 198 * source page or nonexistent 199 * 200 * @fixme This was basically directly moved from Title, it should be split into smaller functions 201 * @param User $user the User doing the move 202 * @param Title $nt The page to move to, which should be a redirect or nonexistent 203 * @param string $reason The reason for the move 204 * @param bool $createRedirect Whether to leave a redirect at the old title. Does not check 205 * if the user has the suppressredirect right 206 * @throws MWException 207 */ 208 private function moveToInternal( User $user, &$nt, $reason = '', $createRedirect = true ) { 209 global $wgContLang; 210 211 if ( $nt->exists() ) { 212 $moveOverRedirect = true; 213 $logType = 'move_redir'; 214 } else { 215 $moveOverRedirect = false; 216 $logType = 'move'; 217 } 218 219 if ( $createRedirect ) { 220 if ( $this->oldTitle->getNamespace() == NS_CATEGORY 221 && !wfMessage( 'category-move-redirect-override' )->inContentLanguage()->isDisabled() 222 ) { 223 $redirectContent = new WikitextContent( 224 wfMessage( 'category-move-redirect-override' ) 225 ->params( $nt->getPrefixedText() )->inContentLanguage()->plain() ); 226 } else { 227 $contentHandler = ContentHandler::getForTitle( $this->oldTitle ); 228 $redirectContent = $contentHandler->makeRedirectContent( $nt, 229 wfMessage( 'move-redirect-text' )->inContentLanguage()->plain() ); 230 } 231 232 // NOTE: If this page's content model does not support redirects, $redirectContent will be null. 233 } else { 234 $redirectContent = null; 235 } 236 237 // bug 57084: log_page should be the ID of the *moved* page 238 $oldid = $this->oldTitle->getArticleID(); 239 $logTitle = clone $this->oldTitle; 240 241 $logEntry = new ManualLogEntry( 'move', $logType ); 242 $logEntry->setPerformer( $user ); 243 $logEntry->setTarget( $logTitle ); 244 $logEntry->setComment( $reason ); 245 $logEntry->setParameters( array( 246 '4::target' => $nt->getPrefixedText(), 247 '5::noredir' => $redirectContent ? '0': '1', 248 ) ); 249 250 $formatter = LogFormatter::newFromEntry( $logEntry ); 251 $formatter->setContext( RequestContext::newExtraneousContext( $this->oldTitle ) ); 252 $comment = $formatter->getPlainActionText(); 253 if ( $reason ) { 254 $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason; 255 } 256 # Truncate for whole multibyte characters. 257 $comment = $wgContLang->truncate( $comment, 255 ); 258 259 $dbw = wfGetDB( DB_MASTER ); 260 261 $newpage = WikiPage::factory( $nt ); 262 263 if ( $moveOverRedirect ) { 264 $newid = $nt->getArticleID(); 265 $newcontent = $newpage->getContent(); 266 267 # Delete the old redirect. We don't save it to history since 268 # by definition if we've got here it's rather uninteresting. 269 # We have to remove it so that the next step doesn't trigger 270 # a conflict on the unique namespace+title index... 271 $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ ); 272 273 $newpage->doDeleteUpdates( $newid, $newcontent ); 274 } 275 276 # Save a null revision in the page's history notifying of the move 277 $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true, $user ); 278 if ( !is_object( $nullRevision ) ) { 279 throw new MWException( 'No valid null revision produced in ' . __METHOD__ ); 280 } 281 282 $nullRevision->insertOn( $dbw ); 283 284 # Change the name of the target page: 285 $dbw->update( 'page', 286 /* SET */ array( 287 'page_namespace' => $nt->getNamespace(), 288 'page_title' => $nt->getDBkey(), 289 ), 290 /* WHERE */ array( 'page_id' => $oldid ), 291 __METHOD__ 292 ); 293 294 // clean up the old title before reset article id - bug 45348 295 if ( !$redirectContent ) { 296 WikiPage::onArticleDelete( $this->oldTitle ); 297 } 298 299 $this->oldTitle->resetArticleID( 0 ); // 0 == non existing 300 $nt->resetArticleID( $oldid ); 301 $newpage->loadPageData( WikiPage::READ_LOCKING ); // bug 46397 302 303 $newpage->updateRevisionOn( $dbw, $nullRevision ); 304 305 wfRunHooks( 'NewRevisionFromEditComplete', 306 array( $newpage, $nullRevision, $nullRevision->getParentId(), $user ) ); 307 308 $newpage->doEditUpdates( $nullRevision, $user, array( 'changed' => false ) ); 309 310 if ( !$moveOverRedirect ) { 311 WikiPage::onArticleCreate( $nt ); 312 } 313 314 # Recreate the redirect, this time in the other direction. 315 if ( $redirectContent ) { 316 $redirectArticle = WikiPage::factory( $this->oldTitle ); 317 $redirectArticle->loadFromRow( false, WikiPage::READ_LOCKING ); // bug 46397 318 $newid = $redirectArticle->insertOn( $dbw ); 319 if ( $newid ) { // sanity 320 $this->oldTitle->resetArticleID( $newid ); 321 $redirectRevision = new Revision( array( 322 'title' => $this->oldTitle, // for determining the default content model 323 'page' => $newid, 324 'user_text' => $user->getName(), 325 'user' => $user->getId(), 326 'comment' => $comment, 327 'content' => $redirectContent ) ); 328 $redirectRevision->insertOn( $dbw ); 329 $redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 ); 330 331 wfRunHooks( 'NewRevisionFromEditComplete', 332 array( $redirectArticle, $redirectRevision, false, $user ) ); 333 334 $redirectArticle->doEditUpdates( $redirectRevision, $user, array( 'created' => true ) ); 335 } 336 } 337 338 # Log the move 339 $logid = $logEntry->insert(); 340 $logEntry->publish( $logid ); 341 } 342 343 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |