*/
protected $mLinksUpdated = '19700101000000';
+ const PURGE_CDN_CACHE = 1; // purge CDN cache for page variant URLs
+ const PURGE_CLUSTER_PCACHE = 2; // purge parser cache in the local datacenter
+ const PURGE_GLOBAL_PCACHE = 4; // set page_touched to clear parser cache in all datacenters
+ const PURGE_ALL = 7;
+
/**
* Constructor and clear the article
* @param Title $title Reference to a Title object.
* @return bool
*/
public function hasViewableContent() {
- return $this->exists() || $this->mTitle->isAlwaysKnown();
+ return $this->mTitle->isKnown();
}
/**
*/
public function getContentModel() {
if ( $this->exists() ) {
- // look at the revision's actual content model
- $rev = $this->getRevision();
-
- if ( $rev !== null ) {
- return $rev->getContentModel();
- } else {
- $title = $this->mTitle->getPrefixedDBkey();
- wfWarn( "Page $title exists but has no (visible) revisions!" );
- }
+ $cache = ObjectCache::getMainWANInstance();
+
+ return $cache->getWithSetCallback(
+ $cache->makeKey( 'page', 'content-model', $this->getLatest() ),
+ $cache::TTL_MONTH,
+ function () {
+ $rev = $this->getRevision();
+ if ( $rev ) {
+ // Look at the revision's actual content model
+ return $rev->getContentModel();
+ } else {
+ $title = $this->mTitle->getPrefixedDBkey();
+ wfWarn( "Page $title exists but has no (visible) revisions!" );
+ return $this->mTitle->getContentModel();
+ }
+ }
+ );
}
// use the default model for this page
/**
* Perform the actions of a page purging
+ * @param integer $flags Bitfield of WikiPage::PURGE_* constants
* @return bool
*/
- public function doPurge() {
+ public function doPurge( $flags = self::PURGE_ALL ) {
if ( !Hooks::run( 'ArticlePurge', [ &$this ] ) ) {
return false;
}
- $this->mTitle->invalidateCache();
- // Send purge after above page_touched update was committed
- DeferredUpdates::addUpdate(
- new CdnCacheUpdate( $this->mTitle->getCdnUrls() ),
- DeferredUpdates::PRESEND
- );
+ if ( ( $flags & self::PURGE_GLOBAL_PCACHE ) == self::PURGE_GLOBAL_PCACHE ) {
+ // Set page_touched in the database to invalidate all DC caches
+ $this->mTitle->invalidateCache();
+ } elseif ( ( $flags & self::PURGE_CLUSTER_PCACHE ) == self::PURGE_CLUSTER_PCACHE ) {
+ // Delete the parser options key in the local cluster to invalidate the DC cache
+ ParserCache::singleton()->deleteOptionsKey( $this );
+ // Avoid sending HTTP 304s in ViewAction to the client who just issued the purge
+ $cache = ObjectCache::getLocalClusterInstance();
+ $cache->set(
+ $cache->makeKey( 'page', 'last-dc-purge', $this->getId() ),
+ wfTimestamp( TS_MW ),
+ $cache::TTL_HOUR
+ );
+ }
+
+ if ( ( $flags & self::PURGE_CDN_CACHE ) == self::PURGE_CDN_CACHE ) {
+ // Clear any HTML file cache
+ HTMLFileCache::clearFileCache( $this->getTitle() );
+ // Send purge after any page_touched above update was committed
+ DeferredUpdates::addUpdate(
+ new CdnCacheUpdate( $this->mTitle->getCdnUrls() ),
+ DeferredUpdates::PRESEND
+ );
+ }
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
// @todo move this logic to MessageCache
return true;
}
+ /**
+ * Get the last time a user explicitly purged the page via action=purge
+ *
+ * @return string|bool TS_MW timestamp or false
+ * @since 1.28
+ */
+ public function getLastPurgeTimestamp() {
+ $cache = ObjectCache::getLocalClusterInstance();
+
+ return $cache->get( $cache->makeKey( 'page', 'last-dc-purge', $this->getId() ) );
+ }
+
/**
* Insert a new empty page record for this article.
* This *must* be followed up by creating a revision
*/
public function doEditContent(
Content $content, $summary, $flags = 0, $baseRevId = false,
- User $user = null, $serialFormat = null, $tags = null
+ User $user = null, $serialFormat = null, $tags = []
) {
global $wgUser, $wgUseAutomaticEditSummaries;
+ // Old default parameter for $tags was null
+ if ( $tags === null ) {
+ $tags = [];
+ }
+
// Low-level sanity check
if ( $this->mTitle->getText() === '' ) {
throw new MWException( 'Something is trying to edit an article with an empty title' );
$old_revision = $this->getRevision(); // current revision
$old_content = $this->getContent( Revision::RAW ); // current revision's content
+ if ( $old_content && $old_content->getModel() !== $content->getModel() ) {
+ $tags[] = 'mw-contentmodelchange';
+ }
+
// Provide autosummaries if one is not provided and autosummaries are enabled
if ( $wgUseAutomaticEditSummaries && ( $flags & EDIT_AUTOSUMMARY ) && $summary == '' ) {
$handler = $content->getContentHandler();
$flags |= EDIT_FORCE_BOT;
}
+ $targetContent = $target->getContent();
+ $changingContentModel = $targetContent->getModel() !== $current->getContentModel();
+
// Actually store the edit
$status = $this->doEditContent(
- $target->getContent(),
+ $targetContent,
$summary,
$flags,
$target->getId(),
] ];
}
+ if ( $changingContentModel ) {
+ // If the content model changed during the rollback,
+ // make sure it gets logged to Special:Log/contentmodel
+ $log = new ManualLogEntry( 'contentmodel', 'change' );
+ $log->setPerformer( $guser );
+ $log->setTarget( $this->mTitle );
+ $log->setComment( $summary );
+ $log->setParameters( [
+ '4::oldmodel' => $current->getContentModel(),
+ '5::newmodel' => $targetContent->getModel(),
+ ] );
+
+ $logId = $log->insert( $dbw );
+ $log->publish( $logId );
+ }
+
$revId = $statusRev->getId();
Hooks::run( 'ArticleRollbackComplete', [ $this, $guser, $target, $current ] );
Hooks::run( 'WikiPageDeletionUpdates', [ $this, $content, &$updates ] );
return $updates;
}
+
+ /**
+ * Whether this content displayed on this page
+ * comes from the local database
+ *
+ * @since 1.28
+ * @return bool
+ */
+ public function isLocal() {
+ return true;
+ }
}