X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fpage%2FWikiPage.php;h=e1d9f99ed62b1300e02331fbee381fc0eedf6a5a;hb=80a372f95741f7f187a11b021035189ec940473a;hp=8eba1ccf7b4b7b5f29b17ffa29c41d02ff8550ab;hpb=c0d3fe525947296e66f64a7dd116acb9cdeaec5c;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index 8eba1ccf7b..e1d9f99ed6 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -90,6 +90,14 @@ class WikiPage implements Page, IDBAccessObject { $this->mTitle = $title; } + /** + * Makes sure that the mTitle object is cloned + * to the newly cloned WikiPage. + */ + public function __clone() { + $this->mTitle = clone $this->mTitle; + } + /** * Create a WikiPage object of the appropriate class for the given title. * @@ -496,13 +504,13 @@ class WikiPage implements Page, IDBAccessObject { /** * Loads page_touched and returns a value indicating if it should be used - * @return bool True if not a redirect + * @return bool True if this page exists and is not a redirect */ public function checkTouched() { if ( !$this->mDataLoaded ) { $this->loadPageData(); } - return !$this->mIsRedirect; + return ( $this->mId && !$this->mIsRedirect ); } /** @@ -887,9 +895,13 @@ class WikiPage implements Page, IDBAccessObject { // Update the DB post-send if the page has not cached since now $that = $this; $latest = $this->getLatest(); - DeferredUpdates::addCallableUpdate( function() use ( $that, $retval, $latest ) { - $that->insertRedirectEntry( $retval, $latest ); - } ); + DeferredUpdates::addCallableUpdate( + function () use ( $that, $retval, $latest ) { + $that->insertRedirectEntry( $retval, $latest ); + }, + DeferredUpdates::POSTSEND, + wfGetDB( DB_MASTER ) + ); return $retval; } @@ -1048,9 +1060,9 @@ class WikiPage implements Page, IDBAccessObject { * @param bool $forceParse Force reindexing, regardless of cache settings * @return bool|ParserOutput ParserOutput or false if the revision was not found */ - public function getParserOutput( ParserOptions $parserOptions, $oldid = null, - $forceParse = false ) { - + public function getParserOutput( + ParserOptions $parserOptions, $oldid = null, $forceParse = false + ) { $useParserCache = ( !$forceParse ) && $this->shouldCheckParserCache( $parserOptions, $oldid ); wfDebug( __METHOD__ . @@ -1758,7 +1770,6 @@ class WikiPage implements Page, IDBAccessObject { $revisionId = $revision->insertOn( $dbw ); // Update page_latest and friends to reflect the new revision if ( !$this->updateRevisionOn( $dbw, $revision, null, $meta['oldIsRedirect'] ) ) { - $dbw->rollback( __METHOD__ ); // sanity; this should never happen throw new MWException( "Failed to update page row to use new revision." ); } @@ -1908,7 +1919,6 @@ class WikiPage implements Page, IDBAccessObject { $revisionId = $revision->insertOn( $dbw ); // Update the page record with revision data if ( !$this->updateRevisionOn( $dbw, $revision, 0 ) ) { - $dbw->rollback( __METHOD__ ); // sanity; this should never happen throw new MWException( "Failed to update page row to use new revision." ); } @@ -2863,13 +2873,24 @@ class WikiPage implements Page, IDBAccessObject { return $status; } + // Given the lock above, we can be confident in the title and page ID values + $namespace = $this->getTitle()->getNamespace(); + $dbKey = $this->getTitle()->getDBkey(); + // At this point we are now comitted to returning an OK // status unless some DB query error or other exception comes up. // This way callers don't have to call rollback() if $status is bad // unless they actually try to catch exceptions (which is rare). // we need to remember the old content so we can use it to generate all deletion updates. - $content = $this->getContent( Revision::RAW ); + try { + $content = $this->getContent( Revision::RAW ); + } catch ( Exception $ex ) { + wfLogWarning( __METHOD__ . ': failed to load content during deletion! ' + . $ex->getMessage() ); + + $content = null; + } // Bitfields to further suppress the content if ( $suppress ) { @@ -2883,57 +2904,58 @@ class WikiPage implements Page, IDBAccessObject { $bitfield = 'rev_deleted'; } - /** - * For now, shunt the revision data into the archive table. - * Text is *not* removed from the text table; bulk storage - * is left intact to avoid breaking block-compression or - * immutable storage schemes. - * - * For backwards compatibility, note that some older archive - * table entries will have ar_text and ar_flags fields still. - * - * In the future, we may keep revisions and mark them with - * the rev_deleted field, which is reserved for this purpose. - */ - - $row = [ - 'ar_namespace' => 'page_namespace', - 'ar_title' => 'page_title', - 'ar_comment' => 'rev_comment', - 'ar_user' => 'rev_user', - 'ar_user_text' => 'rev_user_text', - 'ar_timestamp' => 'rev_timestamp', - 'ar_minor_edit' => 'rev_minor_edit', - 'ar_rev_id' => 'rev_id', - 'ar_parent_id' => 'rev_parent_id', - 'ar_text_id' => 'rev_text_id', - 'ar_text' => '\'\'', // Be explicit to appease - 'ar_flags' => '\'\'', // MySQL's "strict mode"... - 'ar_len' => 'rev_len', - 'ar_page_id' => 'page_id', - 'ar_deleted' => $bitfield, - 'ar_sha1' => 'rev_sha1', - ]; + // For now, shunt the revision data into the archive table. + // Text is *not* removed from the text table; bulk storage + // is left intact to avoid breaking block-compression or + // immutable storage schemes. + // In the future, we may keep revisions and mark them with + // the rev_deleted field, which is reserved for this purpose. - if ( $wgContentHandlerUseDB ) { - $row['ar_content_model'] = 'rev_content_model'; - $row['ar_content_format'] = 'rev_content_format'; - } - - // Copy all the page revisions into the archive table - $dbw->insertSelect( - 'archive', - [ 'page', 'revision' ], - $row, - [ - 'page_id' => $id, - 'page_id = rev_page' - ], - __METHOD__ + // Get all of the page revisions + $res = $dbw->select( + 'revision', + Revision::selectFields(), + [ 'rev_page' => $id ], + __METHOD__, + 'FOR UPDATE' ); + // Build their equivalent archive rows + $rowsInsert = []; + foreach ( $res as $row ) { + $rowInsert = [ + 'ar_namespace' => $namespace, + 'ar_title' => $dbKey, + 'ar_comment' => $row->rev_comment, + 'ar_user' => $row->rev_user, + 'ar_user_text' => $row->rev_user_text, + 'ar_timestamp' => $row->rev_timestamp, + 'ar_minor_edit' => $row->rev_minor_edit, + 'ar_rev_id' => $row->rev_id, + 'ar_parent_id' => $row->rev_parent_id, + 'ar_text_id' => $row->rev_text_id, + 'ar_text' => '', + 'ar_flags' => '', + 'ar_len' => $row->rev_len, + 'ar_page_id' => $id, + 'ar_deleted' => $bitfield, + 'ar_sha1' => $row->rev_sha1, + ]; + if ( $wgContentHandlerUseDB ) { + $rowInsert['ar_content_model'] = $row->rev_content_model; + $rowInsert['ar_content_format'] = $row->rev_content_format; + } + $rowsInsert[] = $rowInsert; + } + // Copy them into the archive table + $dbw->insert( 'archive', $rowsInsert, __METHOD__ ); // Save this so we can pass it to the ArticleDeleteComplete hook. $archivedRevisionCount = $dbw->affectedRows(); + // Clone the title and wikiPage, so we have the information we need when + // we log and run the ArticleDeleteComplete hook. + $logTitle = clone $this->mTitle; + $wikiPageBeforeDelete = clone $this; + // Now that it's safely backed up, delete it $dbw->delete( 'page', [ 'page_id' => $id ], __METHOD__ ); @@ -2941,9 +2963,6 @@ class WikiPage implements Page, IDBAccessObject { $dbw->delete( 'revision', [ 'rev_page' => $id ], __METHOD__ ); } - // Clone the title, so we have the information we need when we log - $logTitle = clone $this->mTitle; - // Log the deletion, if the page was suppressed, put it in the suppression log instead $logtype = $suppress ? 'suppress' : 'delete'; @@ -2962,8 +2981,15 @@ class WikiPage implements Page, IDBAccessObject { $this->doDeleteUpdates( $id, $content ); - Hooks::run( 'ArticleDeleteComplete', - [ &$this, &$user, $reason, $id, $content, $logEntry, $archivedRevisionCount ] ); + Hooks::run( 'ArticleDeleteComplete', [ + &$wikiPageBeforeDelete, + &$user, + $reason, + $id, + $content, + $logEntry, + $archivedRevisionCount + ] ); $status->value = $logid; // Show log excerpt on 404 pages rather than just a link @@ -3005,8 +3031,16 @@ class WikiPage implements Page, IDBAccessObject { * may already return null when the page proper was deleted. */ public function doDeleteUpdates( $id, Content $content = null ) { + try { + $countable = $this->isCountable(); + } catch ( Exception $ex ) { + // fallback for deleting broken pages for which we cannot load the content for + // some reason. Note that doDeleteArticleReal() already logged this problem. + $countable = false; + } + // Update site status - DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, - (int)$this->isCountable(), -1 ) ); + DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, - (int)$countable, -1 ) ); // Delete pagelinks, update secondary indexes, etc $updates = $this->getDeletionUpdates( $content ); @@ -3622,7 +3656,14 @@ class WikiPage implements Page, IDBAccessObject { if ( !$content ) { // load content object, which may be used to determine the necessary updates. // XXX: the content may not be needed to determine the updates. - $content = $this->getContent( Revision::RAW ); + try { + $content = $this->getContent( Revision::RAW ); + } catch ( Exception $ex ) { + // If we can't load the content, something is wrong. Perhaps that's why + // the user is trying to delete the page, so let's not fail in that case. + // Note that doDeleteArticleReal() will already have logged an issue with + // loading the content. + } } if ( !$content ) {