mPage->makeParserOptions(). */ public $mParserOptions; /** * @var Content|null Content of the main slot of $this->mRevisionRecord. * @note This variable is read only, setting it has no effect. * Extensions that wish to override the output of Article::view should use a hook. * @todo MCR: Remove in 1.33 * @deprecated since 1.32 * @since 1.21 */ public $mContentObject; /** * @var bool Is the target revision loaded? Set by fetchRevisionRecord(). * * @deprecated since 1.32. Whether content has been loaded should not be relevant to * code outside this class. */ public $mContentLoaded = false; /** * @var int|null The oldid of the article that was requested to be shown, * 0 for the current revision. * @see $mRevIdFetched */ public $mOldId; /** @var Title|null Title from which we were redirected here, if any. */ public $mRedirectedFrom = null; /** @var string|bool URL to redirect to or false if none */ public $mRedirectUrl = false; /** * @var int Revision ID of revision that was loaded. * @see $mOldId * @deprecated since 1.32, use getRevIdFetched() instead. */ public $mRevIdFetched = 0; /** * @var Status|null represents the outcome of fetchRevisionRecord(). * $fetchResult->value is the RevisionRecord object, if the operation was successful. * * The information in $fetchResult is duplicated by the following deprecated public fields: * $mRevIdFetched, $mContentLoaded (and $mContentObject) also typically duplicate * information of the loaded revision, but may be overwritten by extensions or due to errors. */ private $fetchResult = null; /** * @var ParserOutput|null|false The ParserOutput generated for viewing the page, * initialized by view(). If no ParserOutput could be generated, this is set to false. * @deprecated since 1.32 */ public $mParserOutput = null; /** * @var bool Whether render() was called. With the way subclasses work * here, there doesn't seem to be any other way to stop calling * OutputPage::enableSectionEditLinks() and still have it work as it did before. */ protected $viewIsRenderAction = false; /** * @var LinkRenderer */ protected $linkRenderer; /** * @var PermissionManager */ private $permManager; /** * @var RevisionStore */ private $revisionStore; /* * @var RevisionRecord|null Revision to be shown * * Initialized by getOldIDFromRequest() or fetchRevisionRecord(). Normally loaded from the * database, but may be replaced by an extension, or be a fake representing an error message * or some such. While the output of Article::view is typically based on this revision, * it may be overwritten by error messages or replaced by extensions. * * Replaced $mRevision, which was public and is provided in a deprecated manner via * __get and __set */ private $mRevisionRecord = null; /** * @param Title $title * @param int|null $oldId Revision ID, null to fetch from request, zero for current */ public function __construct( Title $title, $oldId = null ) { $this->mOldId = $oldId; $this->mPage = $this->newPage( $title ); $services = MediaWikiServices::getInstance(); $this->linkRenderer = $services->getLinkRenderer(); $this->permManager = $services->getPermissionManager(); $this->revisionStore = $services->getRevisionStore(); } /** * @param Title $title * @return WikiPage */ protected function newPage( Title $title ) { return new WikiPage( $title ); } /** * Constructor from a page id * @param int $id Article ID to load * @return Article|null */ public static function newFromID( $id ) { $t = Title::newFromID( $id ); return $t == null ? null : new static( $t ); } /** * Create an Article object of the appropriate class for the given page. * * @param Title $title * @param IContextSource $context * @return Article */ public static function newFromTitle( $title, IContextSource $context ) { if ( NS_MEDIA == $title->getNamespace() ) { // XXX: This should not be here, but where should it go? $title = Title::makeTitle( NS_FILE, $title->getDBkey() ); } $page = null; Hooks::runner()->onArticleFromTitle( $title, $page, $context ); if ( !$page ) { switch ( $title->getNamespace() ) { case NS_FILE: $page = new ImagePage( $title ); break; case NS_CATEGORY: $page = new CategoryPage( $title ); break; default: $page = new Article( $title ); } } $page->setContext( $context ); return $page; } /** * Create an Article object of the appropriate class for the given page. * * @param WikiPage $page * @param IContextSource $context * @return Article */ public static function newFromWikiPage( WikiPage $page, IContextSource $context ) { $article = self::newFromTitle( $page->getTitle(), $context ); $article->mPage = $page; // override to keep process cached vars return $article; } /** * Get the page this view was redirected from * @return Title|null * @since 1.28 */ public function getRedirectedFrom() { return $this->mRedirectedFrom; } /** * Tell the page view functions that this view was redirected * from another page on the wiki. * @param Title $from */ public function setRedirectedFrom( Title $from ) { $this->mRedirectedFrom = $from; } /** * Get the title object of the article * * @return Title Title object of this page */ public function getTitle() { return $this->mPage->getTitle(); } /** * Get the WikiPage object of this instance * * @since 1.19 * @return WikiPage */ public function getPage() { return $this->mPage; } /** * Clear the object */ public function clear() { $this->mContentLoaded = false; $this->mRedirectedFrom = null; # Title object if set $this->mRevIdFetched = 0; $this->mRedirectUrl = false; $this->mRevisionRecord = null; $this->mContentObject = null; $this->fetchResult = null; // TODO hard-deprecate direct access to public fields $this->mPage->clear(); } /** * Returns a Content object representing the pages effective display content, * not necessarily the revision's content! * * Note that getContent does not follow redirects anymore. * If you need to fetch redirectable content easily, try * the shortcut in WikiPage::getRedirectTarget() * * This function has side effects! Do not use this function if you * only want the real revision text if any. * * @deprecated since 1.32, use fetchRevisionRecord() instead. * * @return Content * * @since 1.21 */ protected function getContentObject() { if ( $this->mPage->getId() === 0 ) { $content = $this->getSubstituteContent(); } else { $this->fetchContentObject(); $content = $this->mContentObject; } return $content; } /** * Returns Content object to use when the page does not exist. * * @return Content */ private function getSubstituteContent() { # If this is a MediaWiki:x message, then load the messages # and return the message value for x. if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) { $text = $this->getTitle()->getDefaultMessageText(); if ( $text === false ) { $text = ''; } $content = ContentHandler::makeContent( $text, $this->getTitle() ); } else { $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon'; $content = new MessageContent( $message, null ); } return $content; } /** * Returns ParserOutput to use when a page does not exist. In some cases, we still want to show * "virtual" content, e.g. in the MediaWiki namespace, or in the File namespace for non-local * files. * * @param ParserOptions $options * * @return ParserOutput */ protected function getEmptyPageParserOutput( ParserOptions $options ) { $content = $this->getSubstituteContent(); return $content->getParserOutput( $this->getTitle(), 0, $options ); } /** * @see getOldIDFromRequest() * @see getRevIdFetched() * * @return int The oldid of the article that is was requested in the constructor or via the * context's WebRequest. */ public function getOldID() { if ( $this->mOldId === null ) { $this->mOldId = $this->getOldIDFromRequest(); } return $this->mOldId; } /** * Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect * * @return int The old id for the request */ public function getOldIDFromRequest() { $this->mRedirectUrl = false; $request = $this->getContext()->getRequest(); $oldid = $request->getIntOrNull( 'oldid' ); if ( $oldid === null ) { return 0; } if ( $oldid !== 0 ) { # Load the given revision and check whether the page is another one. # In that case, update this instance to reflect the change. if ( $oldid === $this->mPage->getLatest() ) { $this->mRevisionRecord = $this->mPage->getRevisionRecord(); } else { $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid ); if ( $this->mRevisionRecord !== null ) { $revPageId = $this->mRevisionRecord->getPageId(); // Revision title doesn't match the page title given? if ( $this->mPage->getId() != $revPageId ) { $function = get_class( $this->mPage ) . '::newFromID'; $this->mPage = $function( $revPageId ); } } } } $oldRev = $this->mRevisionRecord; if ( $request->getVal( 'direction' ) == 'next' ) { $nextid = 0; if ( $oldRev ) { $nextRev = $this->revisionStore->getNextRevision( $oldRev ); if ( $nextRev ) { $nextid = $nextRev->getId(); } } if ( $nextid ) { $oldid = $nextid; $this->mRevisionRecord = null; } else { $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' ); } } elseif ( $request->getVal( 'direction' ) == 'prev' ) { $previd = 0; if ( $oldRev ) { $prevRev = $this->revisionStore->getPreviousRevision( $oldRev ); if ( $prevRev ) { $previd = $prevRev->getId(); } } if ( $previd ) { $oldid = $previd; $this->mRevisionRecord = null; } } $this->mRevIdFetched = $this->mRevisionRecord ? $this->mRevisionRecord->getId() : 0; return $oldid; } /** * Get text content object * Does *NOT* follow redirects. * @todo When is this null? * @deprecated since 1.32, use fetchRevisionRecord() instead. * * @note Code that wants to retrieve page content from the database should * use WikiPage::getContent(). * * @return Content|null|bool * * @since 1.21 */ protected function fetchContentObject() { if ( !$this->mContentLoaded ) { $this->fetchRevisionRecord(); } return $this->mContentObject; } /** * Fetches the revision to work on. * The revision is typically loaded from the database, but may also be a fake representing * an error message or content supplied by an extension. Refer to $this->fetchResult for * the revision actually loaded from the database, and any errors encountered while doing * that. * * Public since 1.35 * * @return RevisionRecord|null */ public function fetchRevisionRecord() { if ( $this->fetchResult ) { return $this->mRevisionRecord; } $this->mContentLoaded = true; $this->mContentObject = null; $oldid = $this->getOldID(); // $this->mRevisionRecord might already be fetched by getOldIDFromRequest() if ( !$this->mRevisionRecord ) { if ( !$oldid ) { $this->mRevisionRecord = $this->mPage->getRevisionRecord(); if ( !$this->mRevisionRecord ) { wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() ); // Just for sanity, output for this case is done by showMissingArticle(). $this->fetchResult = Status::newFatal( 'noarticletext' ); $this->applyContentOverride( $this->makeFetchErrorContent() ); return null; } } else { $this->mRevisionRecord = $this->revisionStore->getRevisionById( $oldid ); if ( !$this->mRevisionRecord ) { wfDebug( __METHOD__ . " failed to load revision, rev_id $oldid" ); $this->fetchResult = Status::newFatal( 'missing-revision', $oldid ); $this->applyContentOverride( $this->makeFetchErrorContent() ); return null; } } } $this->mRevIdFetched = $this->mRevisionRecord->getId(); $this->fetchResult = Status::newGood( $this->mRevisionRecord ); if ( !RevisionRecord::userCanBitfield( $this->mRevisionRecord->getVisibility(), RevisionRecord::DELETED_TEXT, $this->getContext()->getUser() ) ) { wfDebug( __METHOD__ . " failed to retrieve content of revision " . $this->mRevisionRecord->getId() ); // Just for sanity, output for this case is done by showDeletedRevisionHeader(). $this->fetchResult = Status::newFatal( 'rev-deleted-text-permission' ); $this->applyContentOverride( $this->makeFetchErrorContent() ); return null; } // For B/C only $this->mContentObject = $this->mRevisionRecord->getContent( SlotRecord::MAIN, RevisionRecord::FOR_THIS_USER, $this->getContext()->getUser() ); return $this->mRevisionRecord; } /** * Returns a Content object representing any error in $this->fetchContent, or null * if there is no such error. * * @return Content|null */ private function makeFetchErrorContent() { if ( !$this->fetchResult || $this->fetchResult->isOK() ) { return null; } return new MessageContent( $this->fetchResult->getMessage() ); } /** * Applies a content override by constructing a fake Revision object and assigning * it to mRevisionRecord. The fake revision will not have a user, timestamp or summary set. * * @todo This mechanism was created mainly to accommodate extensions that use the * ArticleAfterFetchContentObject. fetchRevisionRecord() presently also uses this mechanism * to report errors, but that could be changed to use $this->fetchResult instead. * * @todo the ArticleAfterFetchContentObject hook was removed; check if this is still needed * * @param Content $override Content to be used instead of the actual page content, * coming from an extension or representing an error message. */ private function applyContentOverride( Content $override ) { // Construct a fake revision $rev = new MutableRevisionRecord( $this->getTitle() ); $rev->setContent( SlotRecord::MAIN, $override ); $this->mRevisionRecord = $rev; // For B/C only $this->mContentObject = $override; } /** * Returns true if the currently-referenced revision is the current edit * to this page (and it exists). * @return bool */ public function isCurrent() { # If no oldid, this is the current version. if ( $this->getOldID() == 0 ) { return true; } return $this->mPage->exists() && $this->mRevisionRecord && $this->mRevisionRecord->isCurrent(); } /** * Get the fetched Revision object depending on request parameters or null * on failure. The revision returned may be a fake representing an error message or * wrapping content supplied by an extension. Refer to $this->fetchResult for the * revision actually loaded from the database. * * @deprecated since 1.35 * * @since 1.19 * @return Revision|null */ public function getRevisionFetched() { wfDeprecated( __METHOD__, '1.35' ); $revRecord = $this->fetchRevisionRecord(); return $revRecord ? new Revision( $revRecord ) : null; } /** * Use this to fetch the rev ID used on page views * * Before fetchRevisionRecord was called, this returns the page's latest revision, * regardless of what getOldID() returns. * * @return int Revision ID of last article revision */ public function getRevIdFetched() { if ( $this->fetchResult && $this->fetchResult->isOK() ) { return $this->fetchResult->value->getId(); } else { return $this->mPage->getLatest(); } } /** * This is the default action of the index.php entry point: just view the * page of the given title. */ public function view() { global $wgUseFileCache, $wgCdnMaxageStale; # Get variables from query string # As side effect this will load the revision and update the title # in a revision ID is passed in the request, so this should remain # the first call of this method even if $oldid is used way below. $oldid = $this->getOldID(); $user = $this->getContext()->getUser(); # Another whitelist check in case getOldID() is altering the title $permErrors = $this->permManager->getPermissionErrors( 'read', $user, $this->getTitle() ); if ( count( $permErrors ) ) { wfDebug( __METHOD__ . ": denied on secondary read check" ); throw new PermissionsError( 'read', $permErrors ); } $outputPage = $this->getContext()->getOutput(); # getOldID() may as well want us to redirect somewhere else if ( $this->mRedirectUrl ) { $outputPage->redirect( $this->mRedirectUrl ); wfDebug( __METHOD__ . ": redirecting due to oldid" ); return; } # If we got diff in the query, we want to see a diff page instead of the article. if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) { wfDebug( __METHOD__ . ": showing diff page" ); $this->showDiffPage(); return; } # Set page title (may be overridden by DISPLAYTITLE) $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() ); $outputPage->setArticleFlag( true ); # Allow frames by default $outputPage->allowClickjacking(); $parserCache = MediaWikiServices::getInstance()->getParserCache(); $parserOptions = $this->getParserOptions(); $poOptions = []; # Render printable version, use printable version cache if ( $outputPage->isPrintable() ) { $parserOptions->setIsPrintable( true ); $poOptions['enableSectionEditLinks'] = false; $outputPage->prependHTML( Html::warningBox( $outputPage->msg( 'printableversion-deprecated-warning' )->escaped() ) ); } elseif ( $this->viewIsRenderAction || !$this->isCurrent() || !$this->permManager->quickUserCan( 'edit', $user, $this->getTitle() ) ) { $poOptions['enableSectionEditLinks'] = false; } # Try client and file cache if ( $oldid === 0 && $this->mPage->checkTouched() ) { # Try to stream the output from file cache if ( $wgUseFileCache && $this->tryFileCache() ) { wfDebug( __METHOD__ . ": done file cache" ); # tell wgOut that output is taken care of $outputPage->disable(); $this->mPage->doViewUpdates( $user, $oldid ); return; } } # Should the parser cache be used? $useParserCache = $this->mPage->shouldCheckParserCache( $parserOptions, $oldid ); wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) ); if ( $user->getStubThreshold() ) { MediaWikiServices::getInstance()->getStatsdDataFactory()->increment( 'pcache_miss_stub' ); } $this->showRedirectedFromHeader(); $this->showNamespaceHeader(); # Iterate through the possible ways of constructing the output text. # Keep going until $outputDone is set, or we run out of things to do. $pass = 0; $outputDone = false; $this->mParserOutput = false; while ( !$outputDone && ++$pass ) { switch ( $pass ) { case 1: $this->getHookRunner()->onArticleViewHeader( $this, $outputDone, $useParserCache ); break; case 2: # Early abort if the page doesn't exist if ( !$this->mPage->exists() ) { wfDebug( __METHOD__ . ": showing missing article" ); $this->showMissingArticle(); $this->mPage->doViewUpdates( $user ); return; } # Try the parser cache if ( $useParserCache ) { $this->mParserOutput = $parserCache->get( $this->getPage(), $parserOptions ); if ( $this->mParserOutput !== false ) { if ( $oldid ) { wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink" ); $this->setOldSubtitle( $oldid ); } else { wfDebug( __METHOD__ . ": showing parser cache contents" ); } $outputPage->addParserOutput( $this->mParserOutput, $poOptions ); # Ensure that UI elements requiring revision ID have # the correct version information. $outputPage->setRevisionId( $this->mPage->getLatest() ); # Preload timestamp to avoid a DB hit $cachedTimestamp = $this->mParserOutput->getTimestamp(); if ( $cachedTimestamp !== null ) { $outputPage->setRevisionTimestamp( $cachedTimestamp ); $this->mPage->setTimestamp( $cachedTimestamp ); } $outputDone = true; } } break; case 3: # Are we looking at an old revision $rev = $this->fetchRevisionRecord(); if ( $oldid && $this->fetchResult->isOK() ) { $this->setOldSubtitle( $oldid ); if ( !$this->showDeletedRevisionHeader() ) { wfDebug( __METHOD__ . ": cannot view deleted revision" ); return; } } # Ensure that UI elements requiring revision ID have # the correct version information. $outputPage->setRevisionId( $this->getRevIdFetched() ); # Preload timestamp to avoid a DB hit $outputPage->setRevisionTimestamp( $this->mPage->getTimestamp() ); # Pages containing custom CSS or JavaScript get special treatment if ( $this->getTitle()->isSiteConfigPage() || $this->getTitle()->isUserConfigPage() ) { $dir = $this->getContext()->getLanguage()->getDir(); $lang = $this->getContext()->getLanguage()->getHtmlCode(); $outputPage->wrapWikiMsg( "
' . $redirectToText . '
' . $html . '' . $link . '
' ); } $deleteLogPage = new LogPage( 'delete' ); $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) ); LogEventsList::showLogExtract( $outputPage, 'delete', $title ); } /** * Perform a deletion and output success or failure messages * @param string $reason * @param bool $suppress * @param bool $immediate false allows deleting over time via the job queue * @throws FatalError * @throws MWException */ public function doDelete( $reason, $suppress = false, $immediate = false ) { $error = ''; $context = $this->getContext(); $outputPage = $context->getOutput(); $user = $context->getUser(); $status = $this->mPage->doDeleteArticleReal( $reason, $user, $suppress, null, $error, null, [], 'delete', $immediate ); if ( $status->isOK() ) { $deleted = $this->getTitle()->getPrefixedText(); $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) ); $outputPage->setRobotPolicy( 'noindex,nofollow' ); if ( $status->isGood() ) { $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]'; $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink ); $this->getHookRunner()->onArticleDeleteAfterSuccess( $this->getTitle(), $outputPage ); } else { $outputPage->addWikiMsg( 'delete-scheduled', wfEscapeWikiText( $deleted ) ); } $outputPage->returnToMain( false ); } else { $outputPage->setPageTitle( wfMessage( 'cannotdelete-title', $this->getTitle()->getPrefixedText() ) ); if ( $error == '' ) { $outputPage->wrapWikiTextAsInterface( 'error mw-error-cannotdelete', $status->getWikiText( false, false, $context->getLanguage() ) ); $deleteLogPage = new LogPage( 'delete' ); $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) ); LogEventsList::showLogExtract( $outputPage, 'delete', $this->getTitle() ); } else { $outputPage->addHTML( $error ); } } } /* Caching functions */ /** * checkLastModified returns true if it has taken care of all * output to the client that is necessary for this request. * (that is, it has sent a cached version of the page) * * @return bool True if cached version send, false otherwise */ protected function tryFileCache() { static $called = false; if ( $called ) { wfDebug( "Article::tryFileCache(): called twice!?" ); return false; } $called = true; if ( $this->isFileCacheable() ) { $cache = new HTMLFileCache( $this->getTitle(), 'view' ); if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) { wfDebug( "Article::tryFileCache(): about to load file" ); $cache->loadFromFileCache( $this->getContext() ); return true; } else { wfDebug( "Article::tryFileCache(): starting buffer" ); ob_start( [ &$cache, 'saveToFileCache' ] ); } } else { wfDebug( "Article::tryFileCache(): not cacheable" ); } return false; } /** * Check if the page can be cached * @param int $mode One of the HTMLFileCache::MODE_* constants (since 1.28) * @return bool */ public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) { $cacheable = false; if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) { $cacheable = $this->mPage->getId() && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect(); // Extension may have reason to disable file caching on some pages. if ( $cacheable ) { $cacheable = $this->getHookRunner()->onIsFileCacheable( $this ); } } return $cacheable; } /** #@- */ /** * Lightweight method to get the parser output for a page, checking the parser cache * and so on. Doesn't consider most of the stuff that Article::view() is forced to * consider, so it's not appropriate to use there. * * @since 1.16 (r52326) for LiquidThreads * * @param int|null $oldid Revision ID or null * @param User|null $user The relevant user * @return ParserOutput|bool ParserOutput or false if the given revision ID is not found */ public function getParserOutput( $oldid = null, User $user = null ) { // XXX: bypasses mParserOptions and thus setParserOptions() if ( $user === null ) { $parserOptions = $this->getParserOptions(); } else { $parserOptions = $this->mPage->makeParserOptions( $user ); } return $this->mPage->getParserOutput( $parserOptions, $oldid ); } /** * Override the ParserOptions used to render the primary article wikitext. * * @param ParserOptions $options * @throws MWException If the parser options where already initialized. */ public function setParserOptions( ParserOptions $options ) { if ( $this->mParserOptions ) { throw new MWException( "can't change parser options after they have already been set" ); } // clone, so if $options is modified later, it doesn't confuse the parser cache. $this->mParserOptions = clone $options; } /** * Get parser options suitable for rendering the primary article wikitext * @return ParserOptions */ public function getParserOptions() { if ( !$this->mParserOptions ) { $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() ); } // Clone to allow modifications of the return value without affecting cache return clone $this->mParserOptions; } /** * Sets the context this Article is executed in * * @param IContextSource $context * @since 1.18 */ public function setContext( $context ) { $this->mContext = $context; } /** * Gets the context this Article is executed in * * @return IContextSource * @since 1.18 */ public function getContext() { if ( $this->mContext instanceof IContextSource ) { return $this->mContext; } else { wfDebug( __METHOD__ . " called and \$mContext is null. " . "Return RequestContext::getMain(); for sanity" ); return RequestContext::getMain(); } } /** * @deprecated since 1.35, use Article::getPage() instead * * Use PHP's magic __get handler to handle accessing of * raw WikiPage fields for backwards compatibility, as well as the deprecated $mRevision * * @param string $fname Field name * @return mixed */ public function __get( $fname ) { wfDeprecatedMsg( "Accessing Article::\$$fname is deprecated since MediaWiki 1.35", '1.35' ); if ( $fname === 'mRevision' ) { $record = $this->fetchRevisionRecord(); // Ensure that it is loaded return $record ? new Revision( $record ) : null; } if ( property_exists( $this->mPage, $fname ) ) { return $this->mPage->$fname; } trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE ); } /** * @deprecated since 1.35, use Article::getPage() instead * * Use PHP's magic __set handler to handle setting of * raw WikiPage fields for backwards compatibility, as well as the deprecated $mRevision * * @param string $fname Field name * @param mixed $fvalue New value */ public function __set( $fname, $fvalue ) { wfDeprecatedMsg( "Setting Article::\$$fname is deprecated since MediaWiki 1.35", '1.35' ); if ( $fname === 'mRevision' ) { $this->mRevisionRecord = $fvalue ? $fvalue->getRevisionRecord() : null; return; } if ( property_exists( $this->mPage, $fname ) ) { $this->mPage->$fname = $fvalue; // Note: extensions may want to toss on new fields } elseif ( !in_array( $fname, [ 'mContext', 'mPage' ] ) ) { $this->mPage->$fname = $fvalue; } else { trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE ); } } /** * Call to WikiPage function for backwards compatibility. * @deprecated since 1.35, use WikiPage::exists() instead, * or simply omit the EDIT_UPDATE and EDIT_NEW flags. * To protect against race conditions, * use PageUpdater::grabParentRevision. * * @see WikiPage::checkFlags * @param int $flags * @return int */ public function checkFlags( $flags ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->checkFlags( $flags ); } /** * @deprecated since 1.35, use WikiPage::checkTouched instead * @see WikiPage::checkTouched * @return bool */ public function checkTouched() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->checkTouched(); } /** * Call to WikiPage function for backwards compatibility. * @deprecated since 1.35, use WikiPage::clearPreparedEdit instead * @see WikiPage::clearPreparedEdit */ public function clearPreparedEdit() { wfDeprecated( __METHOD__, '1.35' ); $this->mPage->clearPreparedEdit(); } /** * Call to WikiPage function for backwards compatibility. * @deprecated since 1.35 * @see WikiPage::doDeleteArticleReal * @param string $reason * @param bool $suppress * @param int|null $u1 * @param bool|null $u2 * @param array|string &$error * @param User|null $user * @param array $tags * @param bool $immediate * @return Status */ public function doDeleteArticleReal( $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null, $tags = [], $immediate = false ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->doDeleteArticleReal( $reason, $suppress, $u1, $u2, $error, $user, $tags, 'delete', $immediate ); } /** * @deprecated since 1.35, use WikiPage::doDeleteUpdates instead * @see WikiPage::doDeleteUpdates * @param int $id * @param Content|null $content * @param Revision|null $revision * @param User|null $user */ public function doDeleteUpdates( $id, Content $content = null, $revision = null, User $user = null ) { wfDeprecated( __METHOD__, '1.35' ); $this->mPage->doDeleteUpdates( $id, $content, $revision, $user ); } /** * Call to WikiPage function for backwards compatibility. * @deprecated since 1.35, use PageUpdater::doUpdates instead. * * @see WikiPage::doEditUpdates * @param Revision $revision * @param User $user * @param array $options */ public function doEditUpdates( Revision $revision, User $user, array $options = [] ) { wfDeprecated( __METHOD__, '1.35' ); $this->mPage->doEditUpdates( $revision, $user, $options ); } /** * @deprecated since 1.35, use WikiPage::doPurge instead * @see WikiPage::doPurge * @note In 1.28 (and only 1.28), this took a $flags parameter that * controlled how much purging was done. * @return bool */ public function doPurge() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->doPurge(); } /** * @deprecated since 1.35, use WikiPage::doViewUpdates instead * @see WikiPage::doViewUpdates * @param User $user * @param int $oldid */ public function doViewUpdates( User $user, $oldid = 0 ) { wfDeprecated( __METHOD__, '1.35' ); $this->mPage->doViewUpdates( $user, $oldid ); } /** * @deprecated since 1.35, use WikiPage::exists instead * @see WikiPage::exists * @return bool */ public function exists() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->exists(); } /** * @deprecated since 1.35, use WikiPage::followRedirect instead * @see WikiPage::followRedirect * @return bool|Title|string */ public function followRedirect() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->followRedirect(); } /** * Call to WikiPage function for backwards compatibility. * @see ContentHandler::getActionOverrides * @return array */ public function getActionOverrides() { return $this->mPage->getActionOverrides(); } /** * @deprecated since 1.35, use WikiPage::getAutoDeleteReason instead * @see WikiPage::getAutoDeleteReason * @param bool &$hasHistory * @return string|bool */ public function getAutoDeleteReason( &$hasHistory ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getAutoDeleteReason( $hasHistory ); } /** * @deprecated since 1.35, use WikiPage::getCategories instead * @see WikiPage::getCategories * @return TitleArray */ public function getCategories() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getCategories(); } /** * Call to WikiPage function for backwards compatibility. * @deprecated since 1.35 * @see WikiPage::getComment * @param int $audience * @param User|null $user * @return string|null */ public function getComment( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getComment( $audience, $user ); } /** * @deprecated since 1.35, use WikiPage::getContentHandler instead * @see WikiPage::getContentHandler * @return ContentHandler */ public function getContentHandler() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getContentHandler(); } /** * @deprecated since 1.35, use WikiPage::getContentModel instead * @see WikiPage::getContentModel * @return string */ public function getContentModel() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getContentModel(); } /** * @deprecated since 1.35, use WikiPage::getContributors instead * @see WikiPage::getContributors * @return UserArrayFromResult */ public function getContributors() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getContributors(); } /** * Call to WikiPage function for backwards compatibility. * @deprecated since 1.35 * @see WikiPage::getCreator * @param int $audience * @param User|null $user * @return User|null */ public function getCreator( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getCreator( $audience, $user ); } /** * @deprecated since 1.35, use WikiPage::getDeletionUpdates instead * @see WikiPage::getDeletionUpdates * @param Content|null $content * @return DeferrableUpdate[] */ public function getDeletionUpdates( Content $content = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getDeletionUpdates( $content ); } /** * @deprecated since 1.35, use WikiPage::getHiddenCategories instead * @see WikiPage::getHiddenCategories * @return array */ public function getHiddenCategories() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getHiddenCategories(); } /** * @deprecated since 1.35, use WikiPage::getId instead * @see WikiPage::getId * @return int */ public function getId() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getId(); } /** * @deprecated since 1.35, use WikiPage::getLatest instead * @see WikiPage::getLatest * @return int */ public function getLatest() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getLatest(); } /** * @deprecated since 1.35, use WikiPage::getLinksTimestamp instead * @see WikiPage::getLinksTimestamp * @return string|null */ public function getLinksTimestamp() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getLinksTimestamp(); } /** * @deprecated since 1.35, use WikiPage::getMinorEdit instead * @see WikiPage::getMinorEdit * @return bool */ public function getMinorEdit() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getMinorEdit(); } /** * @deprecated since 1.35, use RevisionStore::getFirstRevision * @return Revision|null */ public function getOldestRevision() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getOldestRevision(); } /** * @deprecated since 1.35, use WikiPage::getRedirectTarget instead * @see WikiPage::getRedirectTarget * @return Title|null */ public function getRedirectTarget() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getRedirectTarget(); } /** * @deprecated since 1.35, use WikiPage::getRedirectURL instead * @see WikiPage::getRedirectURL * @param Title $rt * @return bool|Title|string */ public function getRedirectURL( $rt ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getRedirectURL( $rt ); } /** * Call to WikiPage function for backwards compatibility. * @deprecated since 1.35 * @see WikiPage::getRevision * @return Revision|null */ public function getRevision() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getRevision(); } /** * @deprecated since 1.35, use WikiPage::getTimestamp instead * @see WikiPage::getTimestamp * @return string */ public function getTimestamp() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getTimestamp(); } /** * @deprecated since 1.35, use WikiPage::getTouched instead * @see WikiPage::getTouched * @return string */ public function getTouched() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getTouched(); } /** * Call to WikiPage function for backwards compatibility. * @deprecated since 1.35 * @see WikiPage::getUndoContent * @param Revision $undo * @param Revision|null $undoafter * @return Content|bool */ public function getUndoContent( Revision $undo, Revision $undoafter = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getUndoContent( $undo, $undoafter ); } /** * @deprecated since 1.35, use WikiPage::getUser instead * @see WikiPage::getUser * @param int $audience * @param User|null $user * @return int */ public function getUser( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getUser( $audience, $user ); } /** * @deprecated since 1.35, use WikiPage::getUserText instead * @see WikiPage::getUserText * @param int $audience * @param User|null $user * @return string */ public function getUserText( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->getUserText( $audience, $user ); } /** * @deprecated since 1.35, use WikiPage::hasViewableContent instead * @see WikiPage::hasViewableContent * @return bool */ public function hasViewableContent() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->hasViewableContent(); } /** * @deprecated since 1.35, use WikiPage::insertOn instead * @see WikiPage::insertOn * @param IDatabase $dbw * @param int|null $pageId * @return bool|int */ public function insertOn( $dbw, $pageId = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->insertOn( $dbw, $pageId ); } /** * @see WikiPage::insertProtectNullRevision * @deprecated since 1.35, use WikiPage::insertNullProtectionRevision instead * @param string $revCommentMsg * @param array $limit * @param array $expiry * @param int $cascade * @param string $reason * @param User|null $user * @return Revision|null */ public function insertProtectNullRevision( $revCommentMsg, array $limit, array $expiry, $cascade, $reason, $user = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit, $expiry, $cascade, $reason, $user ); } /** * @deprecated since 1.35, use WikiPage::insertRedirect instead * @see WikiPage::insertRedirect * @return Title|null */ public function insertRedirect() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->insertRedirect(); } /** * @deprecated since 1.35, use WikiPage::insertRedirectEntry instead * @see WikiPage::insertRedirectEntry * @param Title $rt * @param int|null $oldLatest * @return bool */ public function insertRedirectEntry( Title $rt, $oldLatest = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->insertRedirectEntry( $rt, $oldLatest ); } /** * @deprecated since 1.35, use WikiPage::isCountable instead * @see WikiPage::isCountable * @param PreparedEdit|bool $editInfo * @return bool */ public function isCountable( $editInfo = false ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->isCountable( $editInfo ); } /** * @deprecated since 1.35, use WikiPage::isRedirect instead * @see WikiPage::isRedirect * @return bool */ public function isRedirect() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->isRedirect(); } /** * @deprecated since 1.35, use WikiPage::loadFromRow instead * @see WikiPage::loadFromRow * @param object|bool $data * @param string|int $from */ public function loadFromRow( $data, $from ) { wfDeprecated( __METHOD__, '1.35' ); $this->mPage->loadFromRow( $data, $from ); } /** * @deprecated since 1.35, use WikiPage::loadPageData instead * @see WikiPage::loadPageData * @param object|string|int $from */ public function loadPageData( $from = 'fromdb' ) { wfDeprecated( __METHOD__, '1.35' ); $this->mPage->loadPageData( $from ); } /** * @deprecated since 1.35, use WikiPage::lockAndGetLatest instead * @see WikiPage::lockAndGetLatest * @return int */ public function lockAndGetLatest() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->lockAndGetLatest(); } /** * @deprecated since 1.35, use WikiPage::makeParserOptions instead * @see WikiPage::makeParserOptions * @param IContextSource|User|string $context * @return ParserOptions */ public function makeParserOptions( $context ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->makeParserOptions( $context ); } /** * @deprecated since 1.35, use WikiPage::pageDataFromId instead * @see WikiPage::pageDataFromId * @param IDatabase $dbr * @param int $id * @param array $options * @return object|bool */ public function pageDataFromId( $dbr, $id, $options = [] ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->pageDataFromId( $dbr, $id, $options ); } /** * @deprecated since 1.35, use WikiPage::pageDataFromTitle instead * @see WikiPage::pageDataFromTitle * @param IDatabase $dbr * @param Title $title * @param array $options * @return object|bool */ public function pageDataFromTitle( $dbr, $title, $options = [] ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->pageDataFromTitle( $dbr, $title, $options ); } /** * Call to WikiPage function for backwards compatibility. * @deprecated since 1.35 with PreparedEdit. * use @see \MediaWiki\Storage\DerivedPageDataUpdater instead. * * @see WikiPage::prepareContentForEdit * @param Content $content * @param Revision|RevisionRecord|null $revision * @param User|null $user * @param string|null $serialFormat * @param bool $useCache * @return PreparedEdit */ public function prepareContentForEdit( Content $content, $revision = null, User $user = null, $serialFormat = null, $useCache = true ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->prepareContentForEdit( $content, $revision, $user, $serialFormat, $useCache ); } /** * @deprecated since 1.35, use WikiPage::protectDescription instead * @see WikiPage::protectDescription * @param array $limit * @param array $expiry * @return string */ public function protectDescription( array $limit, array $expiry ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->protectDescription( $limit, $expiry ); } /** * @deprecated since 1.35, use WikiPage::protectDescriptionLog instead * @see WikiPage::protectDescriptionLog * @param array $limit * @param array $expiry * @return string */ public function protectDescriptionLog( array $limit, array $expiry ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->protectDescriptionLog( $limit, $expiry ); } /** * @deprecated since 1.35, use WikiPage::replaceSectionAtRev instead * @see WikiPage::replaceSectionAtRev * @param string|int|null|bool $sectionId * @param Content $sectionContent * @param string $sectionTitle * @param int|null $baseRevId * @return Content|null */ public function replaceSectionAtRev( $sectionId, Content $sectionContent, $sectionTitle = '', $baseRevId = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent, $sectionTitle, $baseRevId ); } /** * Call to WikiPage function for backwards compatibility. * @deprecated since 1.35, use WikiPage::replaceSectionAtRev instead * * @see WikiPage::replaceSectionContent * @param string|int|null|bool $sectionId * @param Content $sectionContent * @param string $sectionTitle * @param string|null $edittime * @return Content|null */ public function replaceSectionContent( $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->replaceSectionContent( $sectionId, $sectionContent, $sectionTitle, $edittime ); } /** * @deprecated since 1.35, use WikiPage::setTimestamp instead * @see WikiPage::setTimestamp * @param string $ts */ public function setTimestamp( $ts ) { wfDeprecated( __METHOD__, '1.35' ); $this->mPage->setTimestamp( $ts ); } /** * @deprecated since 1.35, use WikiPage::shouldCheckParserCache instead * @see WikiPage::shouldCheckParserCache * @param ParserOptions $parserOptions * @param int $oldId * @return bool */ public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId ); } /** * @deprecated since 1.35, use WikiPage::supportsSections instead * @see WikiPage::supportsSections * @return bool */ public function supportsSections() { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->supportsSections(); } /** * @deprecated since 1.35, use WikiPage::triggerOpportunisticLinksUpdate instead * @see WikiPage::triggerOpportunisticLinksUpdate * @param ParserOutput $parserOutput */ public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) { wfDeprecated( __METHOD__, '1.35' ); $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput ); } /** * @deprecated since 1.35, use WikiPage::updateCategoryCounts instead * @see WikiPage::updateCategoryCounts * @param array $added * @param array $deleted * @param int $id */ public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) { wfDeprecated( __METHOD__, '1.35' ); $this->mPage->updateCategoryCounts( $added, $deleted, $id ); } /** * Call to WikiPage function for backwards compatibility. * @deprecated since 1.35 * @see WikiPage::updateIfNewerOn * @param IDatabase $dbw * @param Revision $revision * @return bool */ public function updateIfNewerOn( $dbw, $revision ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->updateIfNewerOn( $dbw, $revision ); } /** * @deprecated since 1.35, use WikiPage::updateRedirectOn instead * @see WikiPage::updateRedirectOn * @param IDatabase $dbw * @param Title|null $redirectTitle * @param null|bool $lastRevIsRedirect * @return bool */ public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect ); } /** * @deprecated since 1.35, use WikiPage::updateRevisionOn instead * @see WikiPage::updateRevisionOn * @param IDatabase $dbw * @param Revision $revision * @param int|null $lastRevision * @param bool|null $lastRevIsRedirect * @return bool */ public function updateRevisionOn( $dbw, $revision, $lastRevision = null, $lastRevIsRedirect = null ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->updateRevisionOn( $dbw, $revision, $lastRevision, $lastRevIsRedirect ); } /** * @deprecated since 1.35, use WikiPage::doUpdateRestrictions instead * @param array $limit * @param array $expiry * @param bool &$cascade * @param string $reason * @param User $user * @return Status */ public function doUpdateRestrictions( array $limit, array $expiry, &$cascade, $reason, User $user ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user ); } /** * @deprecated since 1.35, use WikiPage::updateRestrictions * @param array $limit * @param string $reason * @param int &$cascade * @param array $expiry * @return bool */ public function updateRestrictions( $limit = [], $reason = '', &$cascade = 0, $expiry = [] ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $this->getContext()->getUser() ); } /** * @deprecated since 1.35, use WikiPage::doDeleteArticleReal instead * @param string $reason * @param bool $suppress * @param int|null $u1 Unused * @param bool|null $u2 Unused * @param string &$error * @param bool $immediate false allows deleting over time via the job queue * @return bool * @throws FatalError * @throws MWException */ public function doDeleteArticle( $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', $immediate = false ) { wfDeprecated( __METHOD__, '1.35' ); return $this->mPage->doDeleteArticle( $reason, $suppress, $u1, $u2, $error, null, $immediate ); } /** * @deprecated since 1.35 * @param string $fromP * @param string $summary * @param string $token * @param bool $bot * @param array &$resultDetails * @param User|null $user * @return array[] */ public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) { wfDeprecated( __METHOD__, '1.35' ); if ( !$user ) { $user = $this->getContext()->getUser(); } return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user ); } /** * @deprecated since 1.35 * @internal * @param string $fromP * @param string $summary * @param bool $bot * @param array &$resultDetails * @param User|null $guser * @return array */ public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) { wfDeprecated( __METHOD__, '1.35' ); if ( !$guser ) { $guser = $this->getContext()->getUser(); } return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser ); } /** * @deprecated since 1.35, use WikiPage::getAutoDeleteReason instead * * @param bool &$hasHistory * @return mixed */ public function generateReason( &$hasHistory ) { wfDeprecated( __METHOD__, '1.35' ); return $this->getPage()->getAutoDeleteReason( $hasHistory ); } }