/**
* Clear the object
+ * @return void
*/
public function clear() {
$this->mDataLoaded = false;
$this->mDataLoadedFrom = self::DATA_NOT_LOADED;
+ $this->clearCacheFields();
+ }
+
+ /**
+ * Clear the object cache fields
+ * @return void
+ */
+ protected function clearCacheFields() {
$this->mCounter = null;
$this->mRedirectTarget = null; # Title object if set
$this->mLastRevision = null; # Latest revision
$this->mTouched = wfTimestamp( TS_MW, $data->page_touched );
$this->mIsRedirect = intval( $data->page_is_redirect );
$this->mLatest = intval( $data->page_latest );
+ // Bug 37225: $latest may no longer match the cached latest Revision object.
+ // Double-check the ID of any cached latest Revision object for consistency.
+ if ( $this->mLastRevision && $this->mLastRevision->getId() != $this->mLatest ) {
+ $this->mLastRevision = null;
+ $this->mTimestamp = '';
+ }
} else {
$lc->addBadLinkObj( $this->mTitle );
$this->mTitle->loadFromRow( false );
+
+ $this->clearCacheFields();
}
$this->mDataLoaded = true;
}
/**
- * Tests if the article text represents a redirect
+ * Tests if the article content represents a redirect
*
- * @param $text mixed string containing article contents, or boolean
* @return bool
*/
- public function isRedirect( $text = false ) {
- if ( $text === false ) $content = $this->getContent();
- else $content = ContentHandler::makeContent( $text, $this->mTitle ); # TODO: allow model and format to be provided; or better, expect a Content object
-
+ public function isRedirect( ) {
+ $content = $this->getContent();
+ if ( !$content ) return false;
- if ( empty( $content ) ) return false;
- else return $content->isRedirect();
+ return $content->isRedirect();
}
/**
}
wfProfileOut( __METHOD__ );
- if ( $row ) {
- return Revision::newFromRow( $row );
- } else {
- return null;
- }
+ return $row ? Revision::newFromRow( $row ) : null;
}
/**
* @return String|bool The text of the current revision. False on failure
* @deprecated as of 1.WD, getContent() should be used instead.
*/
- public function getRawText() { #@todo: deprecated, replace usage!
+ public function getRawText() {
wfDeprecated( __METHOD__, '1.WD' );
return $this->getText( Revision::RAW );
$oldid = $this->getLatest();
}
- $pool = new PoolWorkArticleView( $this, $parserOptions, $oldid, $useParserCache, null );
+ $pool = new PoolWorkArticleView( $this, $parserOptions, $oldid, $useParserCache );
$pool->execute();
wfProfileOut( __METHOD__ );
}
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ //@todo: move this logic to MessageCache
+
if ( $this->mTitle->exists() ) {
- $text = ContentHandler::getContentText( $this->getContent() );
+ // NOTE: use transclusion text for messages.
+ // This is consistent with MessageCache::getMsgFromNamespace()
+
+ $content = $this->getContent();
+ $text = $content === null ? null : $content->getWikitextForTransclusion();
+
+ if ( $text === null ) $text = false;
} else {
$text = false;
}
$this->mLatest = $revision->getId();
$this->mIsRedirect = (bool)$rt;
# Update the LinkCache.
- LinkCache::singleton()->addGoodLinkObj( $this->getId(), $this->mTitle, $len, $this->mIsRedirect, $this->mLatest );
+ LinkCache::singleton()->addGoodLinkObj( $this->getId(), $this->mTitle, $len, $this->mIsRedirect, $this->mLatest, $revision->getContentModel() );
}
wfProfileOut( __METHOD__ );
public function replaceSection( $section, $text, $sectionTitle = '', $edittime = null ) {
wfDeprecated( __METHOD__, '1.WD' );
- $sectionContent = ContentHandler::makeContent( $text, $this->getTitle() ); #XXX: could make section title, but that's not required.
+ if ( !$this->supportsSections() ) {
+ return null;
+ }
+
+ $sectionContent = ContentHandler::makeContent( $text, $this->getTitle() ); # could even make section title, but that's not required.
$newContent = $this->replaceSectionContent( $section, $sectionContent, $sectionTitle, $edittime );
- return ContentHandler::getContentText( $newContent ); #XXX: unclear what will happen for non-wikitext!
+ return ContentHandler::getContentText( $newContent );
+ }
+
+ /**
+ * Returns true iff this page's content model supports sections.
+ *
+ * @return boolean whether sections are supported.
+ *
+ * @todo: the skin should check this and not offer section functionality if sections are not supported.
+ * @todo: the EditPage should check this and not offer section functionality if sections are not supported.
+ */
+ public function supportsSections() {
+ return $this->getContentHandler()->supportsSections();
}
/**
public function replaceSectionContent( $section, Content $sectionContent, $sectionTitle = '', $edittime = null ) {
wfProfileIn( __METHOD__ );
+ if ( !$this->supportsSections() ) {
+ #XXX: log this?
+ return null;
+ }
+
if ( strval( $section ) == '' ) {
// Whole-page edit; let the whole text through
$newContent = $sectionContent;
$hook_ok = wfRunHooks( 'ArticleContentSave', array( &$this, &$user, &$content, &$summary,
$flags & EDIT_MINOR, null, null, &$flags, &$status ) );
- if ( $hook_ok && !empty( $wgHooks['ArticleSave'] ) ) { # avoid serialization overhead if the hook isn't present
+ if ( $hook_ok && Hooks::isRegistered( 'ArticleSave' ) ) { # avoid serialization overhead if the hook isn't present
$content_text = $content->serialize();
$txt = $content_text; # clone
$hook_ok = wfRunHooks( 'ArticleSave', array( &$this, &$user, &$txt, &$summary,
- $flags & EDIT_MINOR, null, null, &$flags, &$status ) );
+ $flags & EDIT_MINOR, null, null, &$flags, &$status ) ); #TODO: survey extensions using this hook
if ( $txt !== $content_text ) {
# if the text changed, unserialize the new version to create an updated Content object.
'timestamp' => $now,
'content_model' => $content->getModel(),
'content_format' => $serialisation_format,
- ) );
+ ) ); #XXX: pass content object?!
$changed = !$content->equals( $old_content );
if ( $changed ) {
- // TODO: validate!
- if ( $content->isValid() ) {
-
+ if ( !$content->isValid() ) {
+ throw new MWException( "New content failed validity check!" );
}
$dbw->begin( __METHOD__ );
return $status;
}
- // TODO: create content diff to pass to update objects that might need it
-
# Update links tables, site stats, etc.
$this->doEditUpdates(
$revision,
$edit->revid = $revid;
$edit->pstContent = $content->preSaveTransform( $this->mTitle, $user, $popts );
- $edit->pst = $edit->pstContent->serialize( $serialization_format );
+ $edit->pst = $edit->pstContent->serialize( $serialization_format ); #XXX: do we need this??
$edit->format = $serialization_format;
$edit->popts = $this->makeParserOptions( 'canonical' );
$edit->newContent = $content;
$edit->oldContent = $this->getContent( Revision::RAW );
- $edit->newText = ContentHandler::getContentText( $edit->newContent ); #FIXME: B/C only! don't use this field!
- $edit->oldText = $edit->oldContent ? ContentHandler::getContentText( $edit->oldContent ) : ''; #FIXME: B/C only! don't use this field!
+ #NOTE: B/C for hooks! don't use these fields!
+ $edit->newText = ContentHandler::getContentText( $edit->newContent );
+ $edit->oldText = $edit->oldContent ? ContentHandler::getContentText( $edit->oldContent ) : '';
$this->mPreparedEdit = $edit;
}
# Update the links tables and other secondary data
- $updates = $editInfo->output->getSecondaryDataUpdates( $this->getTitle() );
+ $contentHandler = $revision->getContentHandler();
+ $updates = $contentHandler->getSecondaryDataUpdates( $content, $this->getTitle(), null, true, $editInfo->output );
DataUpdate::runUpdates( $updates );
wfRunHooks( 'ArticleEditUpdates', array( &$this, &$editInfo, $options['changed'] ) );
}
DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, $good, $total ) );
- DeferredUpdates::addUpdate( new SearchUpdate( $id, $title, $content->getTextForSearchIndex() ) );
+ DeferredUpdates::addUpdate( new SearchUpdate( $id, $title, $content->getTextForSearchIndex() ) ); #TODO: let the search engine decide what to do with the content object
# If this is another user's talk page, update newtalk.
# Don't do this if $options['changed'] = false (null-edits) nor if
}
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- $msgtext = ContentHandler::getContentText( $content ); #XXX: could skip pseudo-messages like js/css here, based on content model.
+ $msgtext = $content->getWikitextForTransclusion(); #XXX: could skip pseudo-messages like js/css here, based on content model.
if ( $msgtext === false || $msgtext === null ) $msgtext = '';
MessageCache::singleton()->replace( $shortTitle, $msgtext );
'length' => $content->getSize(),
'comment' => $comment,
'minor_edit' => $minor ? 1 : 0,
- ) );
+ ) ); #XXX: set the content object?
$revision->insertOn( $dbw );
$this->updateRevisionOn( $dbw, $revision );
$bitfield = 'rev_deleted';
}
+ // we need to remember the old content so we can use it to generate all deletion updates.
+ $content = $this->getContent( Revision::RAW );
+
$dbw = wfGetDB( DB_MASTER );
$dbw->begin( __METHOD__ );
// For now, shunt the revision data into the archive table.
return WikiPage::DELETE_NO_REVISIONS;
}
- $this->doDeleteUpdates( $id );
+ $this->doDeleteUpdates( $id, $content );
# Log the deletion, if the page was suppressed, log it at Oversight instead
$logtype = $suppress ? 'suppress' : 'delete';
* Do some database updates after deletion
*
* @param $id Int: page_id value of the page being deleted (B/C, currently unused)
+ * @param $content Content: optional page content to be used when determining the required updates.
+ * This may be needed because $this->getContent() may already return null when the page proper was deleted.
*/
- public function doDeleteUpdates( $id ) {
+ public function doDeleteUpdates( $id, Content $content = null ) {
# update site status
DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, - (int)$this->isCountable(), -1 ) );
# remove secondary indexes, etc
- $updates = $this->getDeletionUpdates( );
+ $updates = $this->getDeletionUpdates( $content );
DataUpdate::runUpdates( $updates );
# Clear caches
* roll back to, e.g. user is the sole contributor. This function
* performs permissions checks on $user, then calls commitRollback()
* to do the dirty work
- *
+ *
* @todo: seperate the business/permission stuff out from backend code
*
* @param $fromP String: Name of the user whose edits to rollback.
* @param $newtext String|null: The submitted text of the page.
* @param $flags Int bitmask: a bitmask of flags submitted for the edit.
* @return string An appropriate autosummary, or an empty string.
+ *
* @deprecated since 1.WD, use ContentHandler::getAutosummary() instead
*/
public static function getAutosummary( $oldtext, $newtext, $flags ) {
* @param &$hasHistory Boolean: whether the page has a history
* @return mixed String containing deletion reason or empty string, or boolean false
* if no revision occurred
- * @deprecated since 1.WD, use ContentHandler::getAutoDeleteReason() instead
*/
public function getAutoDeleteReason( &$hasHistory ) {
- #NOTE: stub for backwards-compatibility.
-
- wfDeprecated( __METHOD__, '1.WD' );
-
- $handler = ContentHandler::getForTitle( $this->getTitle() );
- $handler->getAutoDeleteReason( $this->getTitle(), $hasHistory );
- global $wgContLang;
-
- // Get the last revision
- $rev = $this->getRevision();
-
- if ( is_null( $rev ) ) {
- return false;
- }
-
- // Get the article's contents
- $contents = $rev->getText();
- $blank = false;
-
- // If the page is blank, use the text from the previous revision,
- // which can only be blank if there's a move/import/protect dummy revision involved
- if ( $contents == '' ) {
- $prev = $rev->getPrevious();
-
- if ( $prev ) {
- $contents = $prev->getText();
- $blank = true;
- }
- }
-
- $dbw = wfGetDB( DB_MASTER );
-
- // Find out if there was only one contributor
- // Only scan the last 20 revisions
- $res = $dbw->select( 'revision', 'rev_user_text',
- array( 'rev_page' => $this->getID(), $dbw->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' ),
- __METHOD__,
- array( 'LIMIT' => 20 )
- );
-
- if ( $res === false ) {
- // This page has no revisions, which is very weird
- return false;
- }
-
- $hasHistory = ( $res->numRows() > 1 );
- $row = $dbw->fetchObject( $res );
-
- if ( $row ) { // $row is false if the only contributor is hidden
- $onlyAuthor = $row->rev_user_text;
- // Try to find a second contributor
- foreach ( $res as $row ) {
- if ( $row->rev_user_text != $onlyAuthor ) { // Bug 22999
- $onlyAuthor = false;
- break;
- }
- }
- } else {
- $onlyAuthor = false;
- }
-
- // Generate the summary with a '$1' placeholder
- if ( $blank ) {
- // The current revision is blank and the one before is also
- // blank. It's just not our lucky day
- $reason = wfMsgForContent( 'exbeforeblank', '$1' );
- } else {
- if ( $onlyAuthor ) {
- $reason = wfMsgForContent( 'excontentauthor', '$1', $onlyAuthor );
- } else {
- $reason = wfMsgForContent( 'excontent', '$1' );
- }
- }
-
- if ( $reason == '-' ) {
- // Allow these UI messages to be blanked out cleanly
- return '';
- }
-
- // Replace newlines with spaces to prevent uglyness
- $contents = preg_replace( "/[\n\r]/", ' ', $contents );
- // Calculate the maximum amount of chars to get
- // Max content length = max comment length - length of the comment (excl. $1)
- $maxLength = 255 - ( strlen( $reason ) - 2 );
- $contents = $wgContLang->truncate( $contents, $maxLength );
- // Remove possible unfinished links
- $contents = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $contents );
- // Now replace the '$1' placeholder
- $reason = str_replace( '$1', $contents, $reason );
-
- return $reason;
+ return $this->getContentHandler()->getAutoDeleteReason( $this->getTitle(), $hasHistory );
}
/**
public function quickEdit( $text, $comment = '', $minor = 0 ) {
wfDeprecated( __METHOD__, '1.18' );
global $wgUser;
- return $this->doQuickEdit( $text, $wgUser, $comment, $minor );
+ $this->doQuickEdit( $text, $wgUser, $comment, $minor );
}
/**
return $this->isParserCacheUsed( ParserOptions::newFromUser( $wgUser ), $oldid );
}
- public function getDeletionUpdates() {
- $updates = $this->getContentHandler()->getDeletionUpdates( $this );
+ /**
+ * Returns a list of updates to be performed when this page is deleted. The updates should remove any infomration
+ * about this page from secondary data stores such as links tables.
+ *
+ * @param Content|null $content optional Content object for determining the necessary updates
+ * @return Array an array of DataUpdates objects
+ */
+ public function getDeletionUpdates( Content $content = null ) {
+ 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, then this would be overhead.
+ $content = $this->getContent( Revision::RAW );
+ }
+
+ $updates = $this->getContentHandler()->getDeletionUpdates( $content, $this->mTitle );
wfRunHooks( 'WikiPageDeletionUpdates', array( $this, &$updates ) );
return $updates;
}
+
}
class PoolWorkArticleView extends PoolCounterWork {
}
$time = - microtime( true );
- // TODO: page might not have this method? Hard to tell what page is supposed to be here...
$this->parserOutput = $content->getParserOutput( $this->page->getTitle(), $this->revid, $this->parserOptions );
$time += microtime( true );