X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FArticle.php;h=393f770bf79dafe3e6b05c608e738e77247696d4;hb=557a05f3843c486e79e01da32c1baa397a352c9a;hp=3c6e41c6f91b9c91dd0a9b8d10e7716ed26ffc64;hpb=6c2f1f06a23b1702db85b28c7947ed7a9a0cb6f6;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Article.php b/includes/Article.php index 3c6e41c6f9..393f770bf7 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -23,7 +23,7 @@ class Article extends Page { */ /** - * @var RequestContext + * @var IContextSource */ protected $mContext; @@ -32,6 +32,11 @@ class Article extends Page { */ protected $mPage; + /** + * @var ParserOptions: ParserOptions object for $wgUser articles + */ + public $mParserOptions; + var $mContent; // !< var $mContentLoaded = false; // !< var $mOldId; // !< @@ -69,6 +74,10 @@ class Article extends Page { $this->mPage = $this->newPage( $title ); } + /** + * @param $title Title + * @return WikiPage + */ protected function newPage( Title $title ) { return new WikiPage( $title ); } @@ -76,6 +85,7 @@ class Article extends Page { /** * Constructor from a page id * @param $id Int article ID to load + * @return Article|null */ public static function newFromID( $id ) { $t = Title::newFromID( $id ); @@ -88,10 +98,10 @@ class Article extends Page { * Create an Article object of the appropriate class for the given page. * * @param $title Title - * @param $context RequestContext + * @param $context IContextSource * @return Article object */ - public static function newFromTitle( $title, RequestContext $context ) { + public static function newFromTitle( $title, IContextSource $context ) { if ( NS_MEDIA == $title->getNamespace() ) { // FIXME: where should this go? $title = Title::makeTitle( NS_FILE, $title->getDBkey() ); @@ -116,6 +126,19 @@ class Article extends Page { return $page; } + /** + * Create an Article object of the appropriate class for the given page. + * + * @param $page WikiPage + * @param $context IContextSource + * @return Article object + */ + 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; + } + /** * Tell the page view functions that this view was redirected * from another page on the wiki. @@ -126,29 +149,26 @@ class Article extends Page { } /** - * Get the Title object this text redirects to + * Get the title object of the article * - * @param $text string article content containing redirect info - * @return mixed false, Title of in-wiki target, or string with URL - * @deprecated since 1.17 + * @return Title object of this page */ - public function followRedirectText( $text ) { - // recurse through to only get the final target - return $this->getRedirectURL( Title::newFromRedirectRecurse( $text ) ); + public function getTitle() { + return $this->mPage->getTitle(); } /** - * Get the title object of the article - * @return Title object of this page + * Get the WikiPage object of this instance + * + * @since 1.19 + * @return WikiPage */ - public function getTitle() { - return $this->mPage->getTitle(); + public function getPage() { + return $this->mPage; } /** * Clear the object - * @todo FIXME: Shouldn't this be public? - * @private */ public function clear() { $this->mContentLoaded = false; @@ -163,12 +183,12 @@ class Article extends Page { /** * Note that getContent/loadContent do not follow redirects anymore. * If you need to fetch redirectable content easily, try - * the shortcut in Article::followRedirect() + * 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. * - * @return Return the text of this revision + * @return string Return the text of this revision */ public function getContent() { global $wgUser; @@ -190,7 +210,7 @@ class Article extends Page { return $text; } else { - $this->loadContent(); + $this->fetchContent(); wfProfileOut( __METHOD__ ); return $this->mContent; @@ -219,27 +239,43 @@ class Article extends Page { $this->mRedirectUrl = false; - $oldid = $wgRequest->getVal( 'oldid' ); + $oldid = $wgRequest->getIntOrNull( 'oldid' ); - if ( isset( $oldid ) ) { - $oldid = intval( $oldid ); - if ( $wgRequest->getVal( 'direction' ) == 'next' ) { - $nextid = $this->getTitle()->getNextRevisionID( $oldid ); - if ( $nextid ) { - $oldid = $nextid; - } else { - $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' ); - } - } elseif ( $wgRequest->getVal( 'direction' ) == 'prev' ) { - $previd = $this->getTitle()->getPreviousRevisionID( $oldid ); - if ( $previd ) { - $oldid = $previd; + 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->mRevision = $this->mPage->getRevision(); + } else { + $this->mRevision = Revision::newFromId( $oldid ); + if ( $this->mRevision !== null ) { + // Revision title doesn't match the page title given? + if ( $this->mPage->getID() != $this->mRevision->getPage() ) { + $function = array( get_class( $this->mPage ), 'newFromID' ); + $this->mPage = call_user_func( $function, $this->mRevision->getPage() ); + } } } } - if ( !$oldid ) { - $oldid = 0; + if ( $wgRequest->getVal( 'direction' ) == 'next' ) { + $nextid = $this->getTitle()->getNextRevisionID( $oldid ); + if ( $nextid ) { + $oldid = $nextid; + $this->mRevision = null; + } else { + $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' ); + } + } elseif ( $wgRequest->getVal( 'direction' ) == 'prev' ) { + $previd = $this->getTitle()->getPreviousRevisionID( $oldid ); + if ( $previd ) { + $oldid = $previd; + $this->mRevision = null; + } } return $oldid; @@ -247,31 +283,31 @@ class Article extends Page { /** * Load the revision (including text) into this object + * + * @deprecated in 1.19; use fetchContent() */ function loadContent() { - if ( $this->mContentLoaded ) { - return; - } - - wfProfileIn( __METHOD__ ); - - $this->fetchContent( $this->getOldID() ); - - wfProfileOut( __METHOD__ ); + wfDeprecated( __METHOD__, '1.19' ); + $this->fetchContent(); } /** * Get text of an article from database * Does *NOT* follow redirects. * - * @param $oldid Int: 0 for whatever the latest revision is * @return mixed string containing article contents, or false if null */ - function fetchContent( $oldid = 0 ) { + function fetchContent() { if ( $this->mContentLoaded ) { return $this->mContent; } + wfProfileIn( __METHOD__ ); + + $this->mContentLoaded = true; + + $oldid = $this->getOldID(); + # Pre-fill content with error message so that if something # fails we'll have something telling us what we intended. $t = $this->getTitle()->getPrefixedText(); @@ -279,43 +315,39 @@ class Article extends Page { $this->mContent = wfMsgNoTrans( 'missing-article', $t, $d ) ; if ( $oldid ) { - $revision = Revision::newFromId( $oldid ); - if ( $revision === null ) { - wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" ); - return false; - } - // Revision title doesn't match the page title given? - if ( $this->mPage->getID() != $revision->getPage() ) { - $function = array( get_class( $this->mPage ), 'newFromID' ); - $this->mPage = call_user_func( $function, $revision->getPage() ); - if ( !$this->mPage->getId() ) { - wfDebug( __METHOD__ . " failed to get page data linked to revision id $oldid\n" ); + # $this->mRevision might already be fetched by getOldIDFromRequest() + if ( !$this->mRevision ) { + $this->mRevision = Revision::newFromId( $oldid ); + if ( !$this->mRevision ) { + wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" ); + wfProfileOut( __METHOD__ ); return false; } } } else { - if ( $this->mPage->getLatest() === false ) { + if ( !$this->mPage->getLatest() ) { wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" ); + wfProfileOut( __METHOD__ ); return false; } - $revision = $this->mPage->getRevision(); - if ( $revision === null ) { + $this->mRevision = $this->mPage->getRevision(); + if ( !$this->mRevision ) { wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" ); + wfProfileOut( __METHOD__ ); return false; } } // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks. // We should instead work with the Revision object when we need it... - $this->mContent = $revision->getText( Revision::FOR_THIS_USER ); // Loads if user is allowed - - $this->mRevIdFetched = $revision->getId(); - $this->mContentLoaded = true; - $this->mRevision =& $revision; + $this->mContent = $this->mRevision->getText( Revision::FOR_THIS_USER ); // Loads if user is allowed + $this->mRevIdFetched = $this->mRevision->getId(); wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) ); + wfProfileOut( __METHOD__ ); + return $this->mContent; } @@ -324,7 +356,7 @@ class Article extends Page { * @deprecated since 1.18 */ public function forUpdate() { - wfDeprecated( __METHOD__ ); + wfDeprecated( __METHOD__, '1.18' ); } /** @@ -341,6 +373,19 @@ class Article extends Page { return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent(); } + /** + * Get the fetched Revision object depending on request parameters or null + * on failure. + * + * @since 1.19 + * @return Revision|null + */ + public function getRevisionFetched() { + $this->fetchContent(); + + return $this->mRevision; + } + /** * Use this to fetch the rev ID used on page views * @@ -360,14 +405,25 @@ class Article extends Page { */ public function view() { global $wgUser, $wgOut, $wgRequest, $wgParser; - global $wgUseFileCache, $wgUseETag; + global $wgUseFileCache, $wgUseETag, $wgDebugToolbar; wfProfileIn( __METHOD__ ); # 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(); - # getOldID may want us to redirect somewhere else + # Another whitelist check in case getOldID() is altering the title + $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $wgUser ); + if ( count( $permErrors ) ) { + wfDebug( __METHOD__ . ": denied on secondary read check\n" ); + wfProfileOut( __METHOD__ ); + throw new PermissionsError( 'read', $permErrors ); + } + + # getOldID() may as well want us to redirect somewhere else if ( $this->mRedirectUrl ) { $wgOut->redirect( $this->mRedirectUrl ); wfDebug( __METHOD__ . ": redirecting due to oldid\n" ); @@ -376,10 +432,6 @@ class Article extends Page { return; } - $wgOut->setArticleFlag( true ); - # Set page title (may be overridden by DISPLAYTITLE) - $wgOut->setPageTitle( $this->getTitle()->getPrefixedText() ); - # If we got diff in the query, we want to see a diff page instead of the article. if ( $wgRequest->getCheck( 'diff' ) ) { wfDebug( __METHOD__ . ": showing diff page\n" ); @@ -389,22 +441,26 @@ class Article extends Page { return; } + # Set page title (may be overridden by DISPLAYTITLE) + $wgOut->setPageTitle( $this->getTitle()->getPrefixedText() ); + + $wgOut->setArticleFlag( true ); # Allow frames by default $wgOut->allowClickjacking(); $parserCache = ParserCache::singleton(); - $parserOptions = $this->mPage->getParserOptions(); + $parserOptions = $this->getParserOptions(); # Render printable version, use printable version cache if ( $wgOut->isPrintable() ) { $parserOptions->setIsPrintable( true ); $parserOptions->setEditSection( false ); - } elseif ( $wgUseETag && !$this->getTitle()->quickUserCan( 'edit' ) ) { + } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit' ) ) { $parserOptions->setEditSection( false ); } # Try client and file cache - if ( $oldid === 0 && $this->mPage->checkTouched() ) { + if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) { if ( $wgUseETag ) { $wgOut->setETag( $parserCache->getETag( $this, $parserOptions ) ); } @@ -420,25 +476,21 @@ class Article extends Page { wfDebug( __METHOD__ . ": done file cache\n" ); # tell wgOut that output is taken care of $wgOut->disable(); - $this->mPage->viewUpdates(); + $this->mPage->doViewUpdates( $wgUser ); wfProfileOut( __METHOD__ ); return; } } - if ( !$wgUseETag && !$this->getTitle()->quickUserCan( 'edit' ) ) { - $parserOptions->setEditSection( false ); - } - # Should the parser cache be used? - $useParserCache = $this->useParserCache( $oldid ); + $useParserCache = $this->mPage->isParserCacheUsed( $parserOptions, $oldid ); wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" ); if ( $wgUser->getStubThreshold() ) { wfIncrStats( 'pcache_miss_stub' ); } - $wasRedirected = $this->showRedirectedFromHeader(); + $this->showRedirectedFromHeader(); $this->showNamespaceHeader(); # Iterate through the possible ways of constructing the output text. @@ -453,45 +505,45 @@ class Article extends Page { wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) ); break; case 2: + # Early abort if the page doesn't exist + if ( !$this->mPage->exists() ) { + wfDebug( __METHOD__ . ": showing missing article\n" ); + $this->showMissingArticle(); + wfProfileOut( __METHOD__ ); + return; + } + # Try the parser cache if ( $useParserCache ) { $this->mParserOutput = $parserCache->get( $this, $parserOptions ); if ( $this->mParserOutput !== false ) { - wfDebug( __METHOD__ . ": showing parser cache contents\n" ); + if ( $oldid ) { + wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" ); + $this->setOldSubtitle( $oldid ); + } else { + wfDebug( __METHOD__ . ": showing parser cache contents\n" ); + } $wgOut->addParserOutput( $this->mParserOutput ); # Ensure that UI elements requiring revision ID have # the correct version information. $wgOut->setRevisionId( $this->mPage->getLatest() ); - $outputDone = true; # Preload timestamp to avoid a DB hit - if ( isset( $this->mParserOutput->mTimestamp ) ) { - $this->mPage->setTimestamp( $this->mParserOutput->mTimestamp ); + $cachedTimestamp = $this->mParserOutput->getTimestamp(); + if ( $cachedTimestamp !== null ) { + $wgOut->setRevisionTimestamp( $cachedTimestamp ); + $this->mPage->setTimestamp( $cachedTimestamp ); } + $outputDone = true; } } break; case 3: - $text = $this->getContent(); - if ( $text === false || $this->mPage->getID() == 0 ) { - wfDebug( __METHOD__ . ": showing missing article\n" ); - $this->showMissingArticle(); - wfProfileOut( __METHOD__ ); - return; - } - - # Another whitelist check in case oldid is altering the title - if ( !$this->getTitle()->userCanRead() ) { - wfDebug( __METHOD__ . ": denied on secondary read check\n" ); - $wgOut->loginToUse(); - $wgOut->output(); - $wgOut->disable(); - wfProfileOut( __METHOD__ ); - return; - } + # This will set $this->mRevision if needed + $this->fetchContent(); # Are we looking at an old revision - if ( $oldid && !is_null( $this->mRevision ) ) { + if ( $oldid && $this->mRevision ) { $this->setOldSubtitle( $oldid ); if ( !$this->showDeletedRevisionHeader() ) { @@ -499,36 +551,29 @@ class Article extends Page { wfProfileOut( __METHOD__ ); return; } - - # If this "old" version is the current, then try the parser cache... - if ( $oldid === $this->mPage->getLatest() && $this->useParserCache( false ) ) { - $this->mParserOutput = $parserCache->get( $this, $parserOptions ); - if ( $this->mParserOutput ) { - wfDebug( __METHOD__ . ": showing parser cache for current rev permalink\n" ); - $wgOut->addParserOutput( $this->mParserOutput ); - $wgOut->setRevisionId( $this->mPage->getLatest() ); - $outputDone = true; - break; - } - } } # Ensure that UI elements requiring revision ID have # the correct version information. $wgOut->setRevisionId( $this->getRevIdFetched() ); + # Preload timestamp to avoid a DB hit + $wgOut->setRevisionTimestamp( $this->getTimestamp() ); # Pages containing custom CSS or JavaScript get special treatment if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) { wfDebug( __METHOD__ . ": showing CSS/JS source\n" ); $this->showCssOrJsPage(); $outputDone = true; + } elseif( !wfRunHooks( 'ArticleViewCustom', array( $this->mContent, $this->getTitle(), $wgOut ) ) ) { + # Allow extensions do their own custom view for certain pages + $outputDone = true; } else { + $text = $this->getContent(); $rt = Title::newFromRedirectArray( $text ); if ( $rt ) { wfDebug( __METHOD__ . ": showing redirect=no page\n" ); # Viewing a redirect page (e.g. with parameter redirect=no) - # Don't append the subtitle if this was an old revision - $wgOut->addHTML( $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() ) ); + $wgOut->addHTML( $this->viewRedirect( $rt ) ); # Parse just to get categories, displaytitle, etc. $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions ); $wgOut->addParserOutputNoText( $this->mParserOutput ); @@ -540,16 +585,34 @@ class Article extends Page { # Run the parse, protected by a pool counter wfDebug( __METHOD__ . ": doing uncached parse\n" ); - $key = $parserCache->getKey( $this, $parserOptions ); - $poolArticleView = new PoolWorkArticleView( $this, $key, $useParserCache, $parserOptions ); + $poolArticleView = new PoolWorkArticleView( $this, $parserOptions, + $this->getRevIdFetched(), $useParserCache, $this->getContent() ); if ( !$poolArticleView->execute() ) { + $error = $poolArticleView->getError(); + if ( $error ) { + $wgOut->clearHTML(); // for release() errors + $wgOut->enableClientCache( false ); + $wgOut->setRobotPolicy( 'noindex,nofollow' ); + + $errortext = $error->getWikiText( false, 'view-pool-error' ); + $wgOut->addWikiText( '
' . $errortext . '
' ); + } # Connection or timeout error wfProfileOut( __METHOD__ ); return; - } else { - $outputDone = true; } + + $this->mParserOutput = $poolArticleView->getParserOutput(); + $wgOut->addParserOutput( $this->mParserOutput ); + + # Don't cache a dirty ParserOutput object + if ( $poolArticleView->getIsDirty() ) { + $wgOut->setSquidMaxage( 0 ); + $wgOut->addHTML( "\n" ); + } + + $outputDone = true; break; # Should be unreachable, but just in case... default: @@ -557,37 +620,52 @@ class Article extends Page { } } - # Adjust the title if it was set by displaytitle, -{T|}- or language conversion - if ( $this->mParserOutput ) { - $titleText = $this->mParserOutput->getTitleText(); + # Get the ParserOutput actually *displayed* here. + # Note that $this->mParserOutput is the *current* version output. + $pOutput = ( $outputDone instanceof ParserOutput ) + ? $outputDone // object fetched by hook + : $this->mParserOutput; - if ( strval( $titleText ) !== '' ) { - $wgOut->setPageTitle( $titleText ); - } + # Adjust title for main page & pages with displaytitle + if ( $pOutput ) { + $this->adjustDisplayTitle( $pOutput ); } # For the main page, overwrite the element with the con- # tents of 'pagetitle-view-mainpage' instead of the default (if # that's not empty). # This message always exists because it is in the i18n files - if ( $this->getTitle()->equals( Title::newMainPage() ) ) { + if ( $this->getTitle()->isMainPage() ) { $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage(); if ( !$msg->isDisabled() ) { $wgOut->setHTMLTitle( $msg->title( $this->getTitle() )->text() ); } } - # Now that we've filled $this->mParserOutput, we know whether - # there are any __NOINDEX__ tags on the page - $policy = $this->getRobotPolicy( 'view' ); + # Check for any __NOINDEX__ tags on the page using $pOutput + $policy = $this->getRobotPolicy( 'view', $pOutput ); $wgOut->setIndexPolicy( $policy['index'] ); $wgOut->setFollowPolicy( $policy['follow'] ); $this->showViewFooter(); - $this->mPage->viewUpdates(); + $this->mPage->doViewUpdates( $wgUser ); + wfProfileOut( __METHOD__ ); } + /** + * Adjust title for pages with displaytitle, -{T|}- or language conversion + * @param $pOutput ParserOutput + */ + public function adjustDisplayTitle( ParserOutput $pOutput ) { + global $wgOut; + # Adjust the title if it was set by displaytitle, -{T|}- or language conversion + $titleText = $pOutput->getTitleText(); + if ( strval( $titleText ) !== '' ) { + $wgOut->setPageTitle( $titleText ); + } + } + /** * Show a diff page according to current request variables. For use within * Article::view() only, other callers should use the DifferenceEngine class. @@ -602,14 +680,14 @@ class Article extends Page { $unhide = $wgRequest->getInt( 'unhide' ) == 1; $oldid = $this->getOldID(); - $de = new DifferenceEngine( $this->getTitle(), $oldid, $diff, $rcid, $purge, $unhide ); + $de = new DifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide ); // DifferenceEngine directly fetched the revision: $this->mRevIdFetched = $de->mNewid; $de->showDiffPage( $diffOnly ); if ( $diff == 0 || $diff == $this->mPage->getLatest() ) { # Run view updates for current revision only - $this->mPage->viewUpdates(); + $this->mPage->doViewUpdates( $wgUser ); } } @@ -623,7 +701,11 @@ class Article extends Page { protected function showCssOrJsPage() { global $wgOut; - $wgOut->wrapWikiMsg( "<div id='mw-clearyourcache'>\n$1\n</div>", 'clearyourcache' ); + $dir = $this->getContext()->getLanguage()->getDir(); + $lang = $this->getContext()->getLanguage()->getCode(); + + $wgOut->wrapWikiMsg( "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>", + 'clearyourcache' ); // Give hooks a chance to customise the output if ( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->getTitle(), $wgOut ) ) ) { @@ -639,10 +721,11 @@ class Article extends Page { /** * Get the robot policy to be used for the current view * @param $action String the action= GET parameter + * @param $pOutput ParserOutput * @return Array the policy that should be set * TODO: actions other than 'view' */ - public function getRobotPolicy( $action ) { + public function getRobotPolicy( $action, $pOutput ) { global $wgOut, $wgArticleRobotPolicies, $wgNamespaceRobotPolicies; global $wgDefaultRobotPolicy, $wgRequest; @@ -690,12 +773,12 @@ class Article extends Page { self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] ) ); } - if ( $this->getTitle()->canUseNoindex() && is_object( $this->mParserOutput ) && $this->mParserOutput->getIndexPolicy() ) { + if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) { # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates # a final sanity check that we have really got the parser output. $policy = array_merge( $policy, - array( 'index' => $this->mParserOutput->getIndexPolicy() ) + array( 'index' => $pOutput->getIndexPolicy() ) ); } @@ -755,16 +838,14 @@ class Article extends Page { // This is an internally redirected page view. // We'll need a backlink to the source page for navigation. if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) { - $redir = Linker::link( + $redir = Linker::linkKnown( $this->mRedirectedFrom, null, array(), - array( 'redirect' => 'no' ), - array( 'known', 'noclasses' ) + array( 'redirect' => 'no' ) ); - $s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir ); - $wgOut->setSubtitle( $s ); + $wgOut->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) ); // Set the fragment if one was specified in the redirect if ( strval( $this->getTitle()->getFragment() ) != '' ) { @@ -777,6 +858,9 @@ class Article extends Page { 'href' => $this->getTitle()->getLocalURL() ) ); + // Tell $wgOut the user arrived at this article through a redirect + $wgOut->setRedirectedFrom( $this->mRedirectedFrom ); + return true; } } elseif ( $rdfrom ) { @@ -784,8 +868,7 @@ class Article extends Page { // If it was reported from a trusted site, supply a backlink. if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) { $redir = Linker::makeExternalLink( $rdfrom, $rdfrom ); - $s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir ); - $wgOut->setSubtitle( $s ); + $wgOut->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) ); return true; } @@ -812,7 +895,7 @@ class Article extends Page { * Show the footer section of an ordinary page view */ public function showViewFooter() { - global $wgOut, $wgUseTrackbacks; + global $wgOut; # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page if ( $this->getTitle()->getNamespace() == NS_USER_TALK && IP::isValid( $this->getTitle()->getText() ) ) { @@ -823,11 +906,6 @@ class Article extends Page { # chance to mark this new article as patrolled. $this->showPatrolFooter(); - # Trackbacks - if ( $wgUseTrackbacks ) { - $this->addTrackbacks(); - } - wfRunHooks( 'ArticleViewFooter', array( $this ) ); } @@ -846,7 +924,7 @@ class Article extends Page { return; } - $token = $wgUser->editToken( $rcid ); + $token = $wgUser->getEditToken( $rcid ); $wgOut->preventClickjacking(); $wgOut->addHTML( @@ -874,7 +952,7 @@ class Article extends Page { * namespace, show the default message text. To be called from Article::view(). */ public function showMissingArticle() { - global $wgOut, $wgRequest, $wgUser; + global $wgOut, $wgRequest, $wgUser, $wgSend404Code; # Show info in user (talk) namespace. Does the user exist? Is he blocked? if ( $this->getTitle()->getNamespace() == NS_USER || $this->getTitle()->getNamespace() == NS_USER_TALK ) { @@ -883,7 +961,7 @@ class Article extends Page { $user = User::newFromName( $rootPart, false /* allow IP users*/ ); $ip = User::isIP( $rootPart ); - if ( !$user->isLoggedIn() && !$ip ) { # User does not exist + if ( !($user && $user->isLoggedIn()) && !$ip ) { # User does not exist $wgOut->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>", array( 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ) ); } elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked @@ -914,6 +992,18 @@ class Article extends Page { 'msgKey' => array( 'moveddeleted-notice' ) ) ); + if ( !$this->mPage->hasViewableContent() && $wgSend404Code ) { + // If there's no backing content, send a 404 Not Found + // for better machine handling of broken links. + $wgRequest->response()->header( "HTTP/1.1 404 Not Found" ); + } + + $hookResult = wfRunHooks( 'BeforeDisplayNoArticleText', array( $this ) ); + + if ( ! $hookResult ) { + return; + } + # Show error message $oldid = $this->getOldID(); if ( $oldid ) { @@ -936,12 +1026,6 @@ class Article extends Page { } $text = "<div class='noarticletext'>\n$text\n</div>"; - if ( !$this->mPage->hasViewableContent() ) { - // If there's no backing content, send a 404 Not Found - // for better machine handling of broken links. - $wgRequest->response()->header( "HTTP/1.1 404 Not Found" ); - } - $wgOut->addWikiText( $text ); } @@ -987,63 +1071,131 @@ class Article extends Page { } /** - * Execute the uncached parse for action=view + * Generate the navigation links when browsing through an article revisions + * It shows the information as: + * Revision as of \<date\>; view current revision + * \<- Previous version | Next Version -\> + * + * @param $oldid String: revision ID of this article revision */ - public function doViewParse() { - global $wgOut; + public function setOldSubtitle( $oldid = 0 ) { + global $wgLang, $wgOut, $wgUser, $wgRequest; - $oldid = $this->getOldID(); - $parserOptions = $this->mPage->getParserOptions(); + if ( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) { + return; + } - # Render printable version, use printable version cache - $parserOptions->setIsPrintable( $wgOut->isPrintable() ); + $unhide = $wgRequest->getInt( 'unhide' ) == 1; - # Don't show section-edit links on old revisions... this way lies madness. - if ( !$this->isCurrent() || $wgOut->isPrintable() || !$this->getTitle()->quickUserCan( 'edit' ) ) { - $parserOptions->setEditSection( false ); + # Cascade unhide param in links for easy deletion browsing + $extraParams = array(); + if ( $unhide ) { + $extraParams['unhide'] = 1; } - $useParserCache = $this->useParserCache( $oldid ); - $this->outputWikiText( $this->getContent(), $useParserCache, $parserOptions ); + if ( $this->mRevision && $this->mRevision->getId() === $oldid ) { + $revision = $this->mRevision; + } else { + $revision = Revision::newFromId( $oldid ); + } - return true; - } + $timestamp = $revision->getTimestamp(); - /** - * Try to fetch an expired entry from the parser cache. If it is present, - * output it and return true. If it is not present, output nothing and - * return false. This is used as a callback function for - * PoolCounter::executeProtected(). - * - * @return boolean - */ - public function tryDirtyCache() { - global $wgOut; - $parserCache = ParserCache::singleton(); - $options = $this->mPage->getParserOptions(); + $current = ( $oldid == $this->mPage->getLatest() ); + $td = $wgLang->timeanddate( $timestamp, true ); + $tddate = $wgLang->date( $timestamp, true ); + $tdtime = $wgLang->time( $timestamp, true ); - if ( $wgOut->isPrintable() ) { - $options->setIsPrintable( true ); - $options->setEditSection( false ); - } + # Show user links if allowed to see them. If hidden, then show them only if requested... + $userlinks = Linker::revUserTools( $revision, !$unhide ); - $output = $parserCache->getDirty( $this, $options ); + $infomsg = $current && !wfMessage( 'revision-info-current' )->isDisabled() + ? 'revision-info-current' + : 'revision-info'; - if ( $output ) { - wfDebug( __METHOD__ . ": sending dirty output\n" ); - wfDebugLog( 'dirty', "dirty output " . $parserCache->getKey( $this, $options ) . "\n" ); - $wgOut->setSquidMaxage( 0 ); - $this->mParserOutput = $output; - $wgOut->addParserOutput( $output ); - $wgOut->addHTML( "<!-- parser cache is expired, sending anyway due to pool overload-->\n" ); + $wgOut->addSubtitle( "<div id=\"mw-{$infomsg}\">" . wfMessage( $infomsg, + $td )->rawParams( $userlinks )->params( $revision->getID(), $tddate, + $tdtime, $revision->getUser() )->parse() . "</div>" ); - return true; - } else { - wfDebugLog( 'dirty', "dirty missing\n" ); - wfDebug( __METHOD__ . ": no dirty cache\n" ); + $lnk = $current + ? wfMsgHtml( 'currentrevisionlink' ) + : Linker::link( + $this->getTitle(), + wfMsgHtml( 'currentrevisionlink' ), + array(), + $extraParams, + array( 'known', 'noclasses' ) + ); + $curdiff = $current + ? wfMsgHtml( 'diff' ) + : Linker::link( + $this->getTitle(), + wfMsgHtml( 'diff' ), + array(), + array( + 'diff' => 'cur', + 'oldid' => $oldid + ) + $extraParams, + array( 'known', 'noclasses' ) + ); + $prev = $this->getTitle()->getPreviousRevisionID( $oldid ) ; + $prevlink = $prev + ? Linker::link( + $this->getTitle(), + wfMsgHtml( 'previousrevision' ), + array(), + array( + 'direction' => 'prev', + 'oldid' => $oldid + ) + $extraParams, + array( 'known', 'noclasses' ) + ) + : wfMsgHtml( 'previousrevision' ); + $prevdiff = $prev + ? Linker::link( + $this->getTitle(), + wfMsgHtml( 'diff' ), + array(), + array( + 'diff' => 'prev', + 'oldid' => $oldid + ) + $extraParams, + array( 'known', 'noclasses' ) + ) + : wfMsgHtml( 'diff' ); + $nextlink = $current + ? wfMsgHtml( 'nextrevision' ) + : Linker::link( + $this->getTitle(), + wfMsgHtml( 'nextrevision' ), + array(), + array( + 'direction' => 'next', + 'oldid' => $oldid + ) + $extraParams, + array( 'known', 'noclasses' ) + ); + $nextdiff = $current + ? wfMsgHtml( 'diff' ) + : Linker::link( + $this->getTitle(), + wfMsgHtml( 'diff' ), + array(), + array( + 'diff' => 'next', + 'oldid' => $oldid + ) + $extraParams, + array( 'known', 'noclasses' ) + ); - return false; + $cdel = Linker::getRevDeleteLink( $wgUser, $revision, $this->getTitle() ); + if ( $cdel !== '' ) { + $cdel .= ' '; } + + $wgOut->addSubtitle( "<div id=\"mw-revision-nav\">" . $cdel . + wfMsgExt( 'revision-nav', array( 'escapenoentities', 'parsemag', 'replaceafter' ), + $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>" ); } /** @@ -1061,13 +1213,18 @@ class Article extends Page { $target = array( $target ); } - $imageDir = wfUILang()->getDir(); + $lang = $this->getTitle()->getPageLanguage(); + $imageDir = $lang->getDir(); if ( $appendSubtitle ) { $wgOut->appendSubtitle( wfMsgHtml( 'redirectpagesub' ) ); } // the loop prepends the arrow image before the link, so the first case needs to be outside + + /** + * @var $title Title + */ $title = array_shift( $target ); if ( $forceKnown ) { @@ -1077,7 +1234,7 @@ class Article extends Page { } $nextRedirect = $wgStylePath . '/common/images/nextredirect' . $imageDir . '.png'; - $alt = wfUILang()->isRTL() ? '←' : '→'; + $alt = $lang->isRTL() ? '←' : '→'; // Automatically append redirect=no to each link, since most of them are redirect pages themselves. foreach ( $target as $rt ) { $link .= Html::element( 'img', array( 'src' => $nextRedirect, 'alt' => $alt ) ); @@ -1095,120 +1252,15 @@ class Article extends Page { } /** - * Builds trackback links for article display if $wgUseTrackbacks is set to true + * Handle action=render */ - public function addTrackbacks() { - global $wgOut; - - $dbr = wfGetDB( DB_SLAVE ); - $tbs = $dbr->select( 'trackbacks', - array( 'tb_id', 'tb_title', 'tb_url', 'tb_ex', 'tb_name' ), - array( 'tb_page' => $this->mPage->getID() ) - ); - - if ( !$dbr->numRows( $tbs ) ) { - return; - } - - $wgOut->preventClickjacking(); - - $tbtext = ""; - foreach ( $tbs as $o ) { - $rmvtxt = ""; - - if ( $this->getContext()->getUser()->isAllowed( 'trackback' ) ) { - $delurl = $this->getTitle()->getFullURL( "action=deletetrackback&tbid=" . - $o->tb_id . "&token=" . urlencode( $this->getContext()->getUser()->editToken() ) ); - $rmvtxt = wfMsg( 'trackbackremove', htmlspecialchars( $delurl ) ); - } - - $tbtext .= "\n"; - $tbtext .= wfMsgNoTrans( strlen( $o->tb_ex ) ? 'trackbackexcerpt' : 'trackback', - $o->tb_title, - $o->tb_url, - $o->tb_ex, - $o->tb_name, - $rmvtxt ); - } - - $wgOut->wrapWikiMsg( "<div id='mw_trackbacks'>\n$1\n</div>\n", array( 'trackbackbox', $tbtext ) ); - } - - /** - * Removes trackback record for current article from trackbacks table - * @deprecated since 1.19 - */ - public function deletetrackback() { - return Action::factory( 'deletetrackback', $this )->show(); - } - - /** - * Handle action=render - */ - - public function render() { + public function render() { global $wgOut; $wgOut->setArticleBodyOnly( true ); $this->view(); } - /** - * Handle action=purge - */ - public function purge() { - return Action::factory( 'purge', $this )->show(); - } - - /** - * Mark this particular edit/page as patrolled - * @deprecated since 1.19 - */ - public function markpatrolled() { - Action::factory( 'markpatrolled', $this )->show(); - } - - /** - * User-interface handler for the "watch" action. - * Requires Request to pass a token as of 1.19. - * @deprecated since 1.18 - */ - public function watch() { - Action::factory( 'watch', $this )->show(); - } - - /** - * Add this page to $wgUser's watchlist - * - * This is safe to be called multiple times - * - * @return bool true on successful watch operation - * @deprecated since 1.18 - */ - public function doWatch() { - global $wgUser; - return WatchAction::doWatch( $this->getTitle(), $wgUser ); - } - - /** - * User interface handler for the "unwatch" action. - * Requires Request to pass a token as of 1.19. - * @deprecated since 1.18 - */ - public function unwatch() { - Action::factory( 'unwatch', $this )->show(); - } - - /** - * Stop watching a page - * @return bool true on successful unwatch - * @deprecated since 1.18 - */ - public function doUnwatch() { - global $wgUser; - return WatchAction::doUnwatch( $this->getTitle(), $wgUser ); - } - /** * action=protect handler */ @@ -1224,217 +1276,70 @@ class Article extends Page { $this->protect(); } - /** - * Info about this page - * Called for ?action=info when $wgAllowPageInfo is on. - */ - public function info() { - Action::factory( 'info', $this )->show(); - } - - /** - * Output a redirect back to the article. - * This is typically used after an edit. - * - * @deprecated in 1.19; call $wgOut->redirect() directly - * @param $noRedir Boolean: add redirect=no - * @param $sectionAnchor String: section to redirect to, including "#" - * @param $extraQuery String: extra query params - */ - public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) { - wfDeprecated( __METHOD__ ); - global $wgOut; - - if ( $noRedir ) { - $query = 'redirect=no'; - if ( $extraQuery ) - $query .= "&$extraQuery"; - } else { - $query = $extraQuery; - } - - $wgOut->redirect( $this->getTitle()->getFullURL( $query ) . $sectionAnchor ); - } - - /** - * Auto-generates a deletion reason - * - * @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 - */ - public function generateReason( &$hasHistory ) { - global $wgContLang; - - $dbw = wfGetDB( DB_MASTER ); - // Get the last revision - $rev = Revision::newFromTitle( $this->getTitle() ); - - 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; - } - } - - // 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->mPage->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; - } - - /** * UI entry point for page deletion */ public function delete() { - global $wgOut, $wgRequest; + global $wgOut, $wgRequest, $wgLang; - $confirm = $wgRequest->wasPosted() && - $this->getContext()->getUser()->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ); - - $this->DeleteReasonList = $wgRequest->getText( 'wpDeleteReasonList', 'other' ); - $this->DeleteReason = $wgRequest->getText( 'wpReason' ); + # This code desperately needs to be totally rewritten - $reason = $this->DeleteReasonList; + $title = $this->getTitle(); + $user = $this->getContext()->getUser(); - if ( $reason != 'other' && $this->DeleteReason != '' ) { - // Entry from drop down menu + additional comment - $reason .= wfMsgForContent( 'colon-separator' ) . $this->DeleteReason; - } elseif ( $reason == 'other' ) { - $reason = $this->DeleteReason; + # Check permissions + $permission_errors = $title->getUserPermissionsErrors( 'delete', $user ); + if ( count( $permission_errors ) ) { + throw new PermissionsError( 'delete', $permission_errors ); } - # Flag to hide all contents of the archived revisions - $suppress = $wgRequest->getVal( 'wpSuppress' ) && $this->getContext()->getUser()->isAllowed( 'suppressrevision' ); - - # This code desperately needs to be totally rewritten - # Read-only check... if ( wfReadOnly() ) { - $wgOut->readOnlyPage(); - - return; - } - - # Check permissions - $permission_errors = $this->getTitle()->getUserPermissionsErrors( 'delete', $this->getContext()->getUser() ); - - if ( count( $permission_errors ) > 0 ) { - $wgOut->showPermissionsErrorPage( $permission_errors ); - - return; + throw new ReadOnlyError; } - $wgOut->setPagetitle( wfMsg( 'delete-confirm', $this->getTitle()->getPrefixedText() ) ); - # Better double-check that it hasn't been deleted yet! $dbw = wfGetDB( DB_MASTER ); - $conds = $this->getTitle()->pageCond(); + $conds = $title->pageCond(); $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ ); if ( $latest === false ) { - $wgOut->showFatalError( - Html::rawElement( - 'div', - array( 'class' => 'error mw-error-cannotdelete' ), - wfMsgExt( 'cannotdelete', array( 'parse' ), - wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) ) - ) - ); + $wgOut->setPageTitle( wfMessage( 'cannotdelete-title', $title->getPrefixedText() ) ); + $wgOut->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>", + array( 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ) + ); $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) ); LogEventsList::showLogExtract( $wgOut, 'delete', - $this->getTitle()->getPrefixedText() + $title->getPrefixedText() ); return; } - # Hack for big sites - $bigHistory = $this->mPage->isBigDeletion(); - if ( $bigHistory && !$this->getTitle()->userCan( 'bigdelete' ) ) { - global $wgLang, $wgDeleteRevisionsLimit; + $deleteReasonList = $wgRequest->getText( 'wpDeleteReasonList', 'other' ); + $deleteReason = $wgRequest->getText( 'wpReason' ); - $wgOut->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n", - array( 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) ); - - return; + if ( $deleteReasonList == 'other' ) { + $reason = $deleteReason; + } elseif ( $deleteReason != '' ) { + // Entry from drop down menu + additional comment + $reason = $deleteReasonList . wfMsgForContent( 'colon-separator' ) . $deleteReason; + } else { + $reason = $deleteReasonList; } - if ( $confirm ) { + if ( $wgRequest->wasPosted() && $user->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), + array( 'delete', $this->getTitle()->getPrefixedText() ) ) ) + { + # Flag to hide all contents of the archived revisions + $suppress = $wgRequest->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' ); + $this->doDelete( $reason, $suppress ); - if ( $wgRequest->getCheck( 'wpWatch' ) && $this->getContext()->getUser()->isLoggedIn() ) { + if ( $wgRequest->getCheck( 'wpWatch' ) && $user->isLoggedIn() ) { $this->doWatch(); - } elseif ( $this->getTitle()->userIsWatching() ) { + } elseif ( $title->userIsWatching() ) { $this->doUnwatch(); } @@ -1448,21 +1353,19 @@ class Article extends Page { } // If the page has a history, insert a warning - if ( $hasHistory && !$confirm ) { - global $wgLang; - - $revisions = $this->mPage->estimateRevisionCount(); + if ( $hasHistory ) { + $revisions = $this->mTitle->estimateRevisionCount(); // @todo FIXME: i18n issue/patchwork message $wgOut->addHTML( '<strong class="mw-delete-warning-revisions">' . wfMsgExt( 'historywarning', array( 'parseinline' ), $wgLang->formatNum( $revisions ) ) . - wfMsgHtml( 'word-separator' ) . Linker::link( $this->getTitle(), + wfMsgHtml( 'word-separator' ) . Linker::link( $title, wfMsgHtml( 'history' ), array( 'rel' => 'archives' ), array( 'action' => 'history' ) ) . '</strong>' ); - if ( $bigHistory ) { + if ( $this->mTitle->isBigDeletion() ) { global $wgDeleteRevisionsLimit; $wgOut->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n", array( 'delete-warning-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) ); @@ -1482,14 +1385,16 @@ class Article extends Page { wfDebug( "Article::confirmDelete\n" ); - $deleteBackLink = Linker::linkKnown( $this->getTitle() ); - $wgOut->setSubtitle( wfMsgHtml( 'delete-backlink', $deleteBackLink ) ); + $wgOut->setPageTitle( wfMessage( 'delete-confirm', $this->getTitle()->getPrefixedText() ) ); + $wgOut->addBacklinkSubtitle( $this->getTitle() ); $wgOut->setRobotPolicy( 'noindex,nofollow' ); $wgOut->addWikiMsg( 'confirmdeletetext' ); wfRunHooks( 'ArticleConfirmDelete', array( $this, $wgOut, &$reason ) ); - if ( $this->getContext()->getUser()->isAllowed( 'suppressrevision' ) ) { + $user = $this->getContext()->getUser(); + + if ( $user->isAllowed( 'suppressrevision' ) ) { $suppress = "<tr id=\"wpDeleteSuppressRow\"> <td></td> <td class='mw-input'><strong>" . @@ -1500,7 +1405,7 @@ class Article extends Page { } else { $suppress = ''; } - $checkWatch = $this->getContext()->getUser()->getBoolOption( 'watchdeletion' ) || $this->getTitle()->userIsWatching(); + $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $this->getTitle()->userIsWatching(); $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) . @@ -1533,7 +1438,7 @@ class Article extends Page { </tr>"; # Disallow watching if user is not logged in - if ( $this->getContext()->getUser()->isLoggedIn() ) { + if ( $user->isLoggedIn() ) { $form .= " <tr> <td></td> @@ -1555,10 +1460,10 @@ class Article extends Page { </tr>" . Xml::closeElement( 'table' ) . Xml::closeElement( 'fieldset' ) . - Html::hidden( 'wpEditToken', $this->getContext()->getUser()->editToken() ) . + Html::hidden( 'wpEditToken', $user->getEditToken( array( 'delete', $this->getTitle()->getPrefixedText() ) ) ) . Xml::closeElement( 'form' ); - if ( $this->getContext()->getUser()->isAllowed( 'editinterface' ) ) { + if ( $user->isAllowed( 'editinterface' ) ) { $title = Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' ); $link = Linker::link( $title, @@ -1578,17 +1483,17 @@ class Article extends Page { /** * Perform a deletion and output success or failure messages + * @param $reason + * @param $suppress bool */ public function doDelete( $reason, $suppress = false ) { global $wgOut; - $id = $this->getTitle()->getArticleID( Title::GAID_FOR_UPDATE ); - $error = ''; - if ( $this->mPage->doDeleteArticle( $reason, $suppress, $id, $error ) ) { + if ( $this->mPage->doDeleteArticle( $reason, $suppress, 0, true, $error ) ) { $deleted = $this->getTitle()->getPrefixedText(); - $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); + $wgOut->setPageTitle( wfMessage( 'actioncomplete' ) ); $wgOut->setRobotPolicy( 'noindex,nofollow' ); $loglink = '[[Special:Log/delete|' . wfMsgNoTrans( 'deletionlog' ) . ']]'; @@ -1596,16 +1501,11 @@ class Article extends Page { $wgOut->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink ); $wgOut->returnToMain( false ); } else { + $wgOut->setPageTitle( wfMessage( 'cannotdelete-title', $this->getTitle()->getPrefixedText() ) ); if ( $error == '' ) { - $wgOut->showFatalError( - Html::rawElement( - 'div', - array( 'class' => 'error mw-error-cannotdelete' ), - wfMsgExt( 'cannotdelete', array( 'parse' ), - wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) ) - ) + $wgOut->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>", + array( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) ) ); - $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) ); LogEventsList::showLogExtract( @@ -1614,492 +1514,236 @@ class Article extends Page { $this->getTitle()->getPrefixedText() ); } else { - $wgOut->showFatalError( $error ); + $wgOut->addHTML( $error ); } } } + /* Caching functions */ + /** - * User interface for rollback operations + * 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 boolean true if cached version send, false otherwise */ - public function rollback() { - global $wgUser, $wgOut, $wgRequest; - - $details = null; - - $result = $this->mPage->doRollback( - $wgRequest->getVal( 'from' ), - $wgRequest->getText( 'summary' ), - $wgRequest->getVal( 'token' ), - $wgRequest->getBool( 'bot' ), - $details - ); + protected function tryFileCache() { + static $called = false; - if ( in_array( array( 'actionthrottledtext' ), $result ) ) { - $wgOut->rateLimited(); - return; + if ( $called ) { + wfDebug( "Article::tryFileCache(): called twice!?\n" ); + return false; } - if ( isset( $result[0][0] ) && ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' ) ) { - $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) ); - $errArray = $result[0]; - $errMsg = array_shift( $errArray ); - $wgOut->addWikiMsgArray( $errMsg, $errArray ); - - if ( isset( $details['current'] ) ) { - $current = $details['current']; - - if ( $current->getComment() != '' ) { - $wgOut->addWikiMsgArray( 'editcomment', array( - Linker::formatComment( $current->getComment() ) ), array( 'replaceafter' ) ); - } + $called = true; + if ( $this->isFileCacheable() ) { + $cache = HTMLFileCache::newFromTitle( $this->getTitle(), 'view' ); + if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) { + wfDebug( "Article::tryFileCache(): about to load file\n" ); + $cache->loadFromFileCache( $this->getContext() ); + return true; + } else { + wfDebug( "Article::tryFileCache(): starting buffer\n" ); + ob_start( array( &$cache, 'saveToFileCache' ) ); } - - return; + } else { + wfDebug( "Article::tryFileCache(): not cacheable\n" ); } - # Display permissions errors before read-only message -- there's no - # point in misleading the user into thinking the inability to rollback - # is only temporary. - if ( !empty( $result ) && $result !== array( array( 'readonlytext' ) ) ) { - # array_diff is completely broken for arrays of arrays, sigh. - # Remove any 'readonlytext' error manually. - $out = array(); - foreach ( $result as $error ) { - if ( $error != array( 'readonlytext' ) ) { - $out [] = $error; - } - } - $wgOut->showPermissionsErrorPage( $out ); + return false; + } - return; + /** + * Check if the page can be cached + * @return bool + */ + public function isFileCacheable() { + $cacheable = false; + + if ( HTMLFileCache::useFileCache( $this->getContext() ) ) { + $cacheable = $this->mPage->getID() + && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect(); + // Extension may have reason to disable file caching on some pages. + if ( $cacheable ) { + $cacheable = wfRunHooks( 'IsFileCacheable', array( &$this ) ); + } } - if ( $result == array( array( 'readonlytext' ) ) ) { - $wgOut->readOnlyPage(); + return $cacheable; + } - return; - } + /**#@-*/ - $current = $details['current']; - $target = $details['target']; - $newId = $details['newid']; - $wgOut->setPageTitle( wfMsg( 'actioncomplete' ) ); - $wgOut->setRobotPolicy( 'noindex,nofollow' ); + /** + * 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 WikiPage::view is forced to + * consider, so it's not appropriate to use there. + * + * @since 1.16 (r52326) for LiquidThreads + * + * @param $oldid mixed integer Revision ID or null + * @param $user User The relevant user + * @return ParserOutput or false if the given revsion ID is not found + */ + public function getParserOutput( $oldid = null, User $user = null ) { + global $wgUser; - if ( $current->getUserText() === '' ) { - $old = wfMsg( 'rev-deleted-user' ); - } else { - $old = Linker::userLink( $current->getUser(), $current->getUserText() ) - . Linker::userToolLinks( $current->getUser(), $current->getUserText() ); - } + $user = is_null( $user ) ? $wgUser : $user; + $parserOptions = $this->mPage->makeParserOptions( $user ); - $new = Linker::userLink( $target->getUser(), $target->getUserText() ) - . Linker::userToolLinks( $target->getUser(), $target->getUserText() ); - $wgOut->addHTML( wfMsgExt( 'rollback-success', array( 'parse', 'replaceafter' ), $old, $new ) ); - $wgOut->returnToMain( false, $this->getTitle() ); + return $this->mPage->getParserOutput( $parserOptions, $oldid ); + } - if ( !$wgRequest->getBool( 'hidediff', false ) && !$wgUser->getBoolOption( 'norollbackdiff', false ) ) { - $de = new DifferenceEngine( $this->getTitle(), $current->getId(), $newId, false, true ); - $de->showDiff( '', '' ); + /** + * Get parser options suitable for rendering the primary article wikitext + * @return ParserOptions + */ + public function getParserOptions() { + global $wgUser; + if ( !$this->mParserOptions ) { + $this->mParserOptions = $this->mPage->makeParserOptions( $wgUser ); } + // Clone to allow modifications of the return value without affecting cache + return clone $this->mParserOptions; } /** - * Generate the navigation links when browsing through an article revisions - * It shows the information as: - * Revision as of \<date\>; view current revision - * \<- Previous version | Next Version -\> + * Sets the context this Article is executed in * - * @param $oldid String: revision ID of this article revision + * @param $context IContextSource + * @since 1.18 */ - public function setOldSubtitle( $oldid = 0 ) { - global $wgLang, $wgOut, $wgUser, $wgRequest; - - if ( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) { - return; - } - - $unhide = $wgRequest->getInt( 'unhide' ) == 1; - - # Cascade unhide param in links for easy deletion browsing - $extraParams = array(); - if ( $wgRequest->getVal( 'unhide' ) ) { - $extraParams['unhide'] = 1; - } - - $revision = Revision::newFromId( $oldid ); - $timestamp = $revision->getTimestamp(); - - $current = ( $oldid == $this->mPage->getLatest() ); - $td = $wgLang->timeanddate( $timestamp, true ); - $tddate = $wgLang->date( $timestamp, true ); - $tdtime = $wgLang->time( $timestamp, true ); - - $lnk = $current - ? wfMsgHtml( 'currentrevisionlink' ) - : Linker::link( - $this->getTitle(), - wfMsgHtml( 'currentrevisionlink' ), - array(), - $extraParams, - array( 'known', 'noclasses' ) - ); - $curdiff = $current - ? wfMsgHtml( 'diff' ) - : Linker::link( - $this->getTitle(), - wfMsgHtml( 'diff' ), - array(), - array( - 'diff' => 'cur', - 'oldid' => $oldid - ) + $extraParams, - array( 'known', 'noclasses' ) - ); - $prev = $this->getTitle()->getPreviousRevisionID( $oldid ) ; - $prevlink = $prev - ? Linker::link( - $this->getTitle(), - wfMsgHtml( 'previousrevision' ), - array(), - array( - 'direction' => 'prev', - 'oldid' => $oldid - ) + $extraParams, - array( 'known', 'noclasses' ) - ) - : wfMsgHtml( 'previousrevision' ); - $prevdiff = $prev - ? Linker::link( - $this->getTitle(), - wfMsgHtml( 'diff' ), - array(), - array( - 'diff' => 'prev', - 'oldid' => $oldid - ) + $extraParams, - array( 'known', 'noclasses' ) - ) - : wfMsgHtml( 'diff' ); - $nextlink = $current - ? wfMsgHtml( 'nextrevision' ) - : Linker::link( - $this->getTitle(), - wfMsgHtml( 'nextrevision' ), - array(), - array( - 'direction' => 'next', - 'oldid' => $oldid - ) + $extraParams, - array( 'known', 'noclasses' ) - ); - $nextdiff = $current - ? wfMsgHtml( 'diff' ) - : Linker::link( - $this->getTitle(), - wfMsgHtml( 'diff' ), - array(), - array( - 'diff' => 'next', - 'oldid' => $oldid - ) + $extraParams, - array( 'known', 'noclasses' ) - ); - - $cdel = ''; - - // User can delete revisions or view deleted revisions... - $canHide = $wgUser->isAllowed( 'deleterevision' ); - if ( $canHide || ( $revision->getVisibility() && $wgUser->isAllowed( 'deletedhistory' ) ) ) { - if ( !$revision->userCan( Revision::DELETED_RESTRICTED ) ) { - $cdel = Linker::revDeleteLinkDisabled( $canHide ); // rev was hidden from Sysops - } else { - $query = array( - 'type' => 'revision', - 'target' => $this->getTitle()->getPrefixedDbkey(), - 'ids' => $oldid - ); - $cdel = Linker::revDeleteLink( $query, $revision->isDeleted( File::DELETED_RESTRICTED ), $canHide ); - } - $cdel .= ' '; - } - - # Show user links if allowed to see them. If hidden, then show them only if requested... - $userlinks = Linker::revUserTools( $revision, !$unhide ); - - $infomsg = $current && !wfMessage( 'revision-info-current' )->isDisabled() - ? 'revision-info-current' - : 'revision-info'; - - $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . - wfMsgExt( - $infomsg, - array( 'parseinline', 'replaceafter' ), - $td, - $userlinks, - $revision->getID(), - $tddate, - $tdtime, - $revision->getUser() - ) . - "</div>\n" . - "\n\t\t\t\t<div id=\"mw-revision-nav\">" . $cdel . wfMsgExt( 'revision-nav', array( 'escapenoentities', 'parsemag', 'replaceafter' ), - $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t"; - - $wgOut->addHTML( $r ); + public function setContext( $context ) { + $this->mContext = $context; } - /* 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) + * Gets the context this Article is executed in * - * @return boolean true if cached version send, false otherwise + * @return IContextSource + * @since 1.18 */ - protected function tryFileCache() { - static $called = false; - - if ( $called ) { - wfDebug( "Article::tryFileCache(): called twice!?\n" ); - return false; - } - - $called = true; - if ( $this->isFileCacheable() ) { - $cache = new HTMLFileCache( $this->getTitle() ); - if ( $cache->isFileCacheGood( $this->mPage->getTouched() ) ) { - wfDebug( "Article::tryFileCache(): about to load file\n" ); - $cache->loadFromFileCache(); - return true; - } else { - wfDebug( "Article::tryFileCache(): starting buffer\n" ); - ob_start( array( &$cache, 'saveToFileCache' ) ); - } + public function getContext() { + if ( $this->mContext instanceof IContextSource ) { + return $this->mContext; } else { - wfDebug( "Article::tryFileCache(): not cacheable\n" ); + wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" ); + return RequestContext::getMain(); } - - return false; } /** - * Check if the page can be cached - * @return bool + * Info about this page + * @deprecated since 1.19 */ - public function isFileCacheable() { - $cacheable = false; - - if ( HTMLFileCache::useFileCache() ) { - $cacheable = $this->mPage->getID() && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect(); - // Extension may have reason to disable file caching on some pages. - if ( $cacheable ) { - $cacheable = wfRunHooks( 'IsFileCacheable', array( &$this ) ); - } - } + public function info() { + wfDeprecated( __METHOD__, '1.19' ); + Action::factory( 'info', $this )->show(); + } - return $cacheable; + /** + * Mark this particular edit/page as patrolled + * @deprecated since 1.18 + */ + public function markpatrolled() { + wfDeprecated( __METHOD__, '1.18' ); + Action::factory( 'markpatrolled', $this )->show(); } - /**#@-*/ + /** + * Handle action=purge + * @deprecated since 1.19 + */ + public function purge() { + return Action::factory( 'purge', $this )->show(); + } /** - * Overriden by ImagePage class, only present here to avoid a fatal error - * Called for ?action=revert + * Handle action=revert + * @deprecated since 1.19 */ public function revert() { - global $wgOut; - $wgOut->showErrorPage( 'nosuchaction', 'nosuchactiontext' ); + wfDeprecated( __METHOD__, '1.19' ); + Action::factory( 'revert', $this )->show(); } /** - * Add the primary page-view wikitext to the output buffer - * Saves the text into the parser cache if possible. - * Updates templatelinks if it is out of date. - * - * @param $text String - * @param $cache Boolean - * @param $parserOptions mixed ParserOptions object, or boolean false + * Handle action=rollback + * @deprecated since 1.19 */ - public function outputWikiText( $text, $cache = true, $parserOptions = false ) { - global $wgOut; - - $this->mParserOutput = $this->getOutputFromWikitext( $text, $cache, $parserOptions ); - - $this->doCascadeProtectionUpdates( $this->mParserOutput ); - - $wgOut->addParserOutput( $this->mParserOutput ); + public function rollback() { + wfDeprecated( __METHOD__, '1.19' ); + Action::factory( 'rollback', $this )->show(); } /** - * 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 WikiPage::view is forced to - * consider, so it's not appropriate to use there. - * - * @since 1.16 (r52326) for LiquidThreads - * - * @param $oldid mixed integer Revision ID or null - * @param $user User The relevant user - * @return ParserOutput or false if the given revsion ID is not found + * User-interface handler for the "watch" action. + * Requires Request to pass a token as of 1.18. + * @deprecated since 1.18 */ - public function getParserOutput( $oldid = null, User $user = null ) { - global $wgEnableParserCache, $wgUser; - $user = is_null( $user ) ? $wgUser : $user; - - // Should the parser cache be used? - $useParserCache = $wgEnableParserCache && - $user->getStubThreshold() == 0 && - $this->mPage->exists() && - $oldid === null; - - wfDebug( __METHOD__ . ': using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" ); - - if ( $user->getStubThreshold() ) { - wfIncrStats( 'pcache_miss_stub' ); - } - - if ( $useParserCache ) { - $parserOutput = ParserCache::singleton()->get( $this, $this->mPage->getParserOptions() ); - if ( $parserOutput !== false ) { - return $parserOutput; - } - } - - // Cache miss; parse and output it. - if ( $oldid === null ) { - $text = $this->mPage->getRawText(); - } else { - $rev = Revision::newFromTitle( $this->getTitle(), $oldid ); - if ( $rev === null ) { - return false; - } - $text = $rev->getText(); - } - - return $this->getOutputFromWikitext( $text, $useParserCache ); + public function watch() { + wfDeprecated( __METHOD__, '1.18' ); + Action::factory( 'watch', $this )->show(); } /** - * This does all the heavy lifting for outputWikitext, except it returns the parser - * output instead of sending it straight to $wgOut. Makes things nice and simple for, - * say, embedding thread pages within a discussion system (LiquidThreads) + * Add this page to $wgUser's watchlist + * + * This is safe to be called multiple times * - * @param $text string - * @param $cache boolean - * @param $parserOptions parsing options, defaults to false - * @return string containing parsed output + * @return bool true on successful watch operation + * @deprecated since 1.18 */ - public function getOutputFromWikitext( $text, $cache = true, $parserOptions = false ) { - global $wgParser, $wgEnableParserCache, $wgUseFileCache; - - if ( !$parserOptions ) { - $parserOptions = $this->mPage->getParserOptions(); - } - - $time = - wfTime(); - $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), - $parserOptions, true, true, $this->getRevIdFetched() ); - $time += wfTime(); - - # Timing hack - if ( $time > 3 ) { - wfDebugLog( 'slow-parse', sprintf( "%-5.2f %s", $time, - $this->getTitle()->getPrefixedDBkey() ) ); - } - - if ( $wgEnableParserCache && $cache && $this->mParserOutput->isCacheable() ) { - $parserCache = ParserCache::singleton(); - $parserCache->save( $this->mParserOutput, $this, $parserOptions ); - } - - // Make sure file cache is not used on uncacheable content. - // Output that has magic words in it can still use the parser cache - // (if enabled), though it will generally expire sooner. - if ( !$this->mParserOutput->isCacheable() || $this->mParserOutput->containsOldMagic() ) { - $wgUseFileCache = false; - } - - $this->doCascadeProtectionUpdates( $this->mParserOutput ); - - return $this->mParserOutput; + public function doWatch() { + global $wgUser; + wfDeprecated( __METHOD__, '1.18' ); + return WatchAction::doWatch( $this->getTitle(), $wgUser ); } /** - * Updates cascading protections - * - * @param $parserOutput ParserOutput object, or boolean false - **/ - protected function doCascadeProtectionUpdates( $parserOutput ) { - if ( !$this->isCurrent() || wfReadOnly() || !$this->getTitle()->areRestrictionsCascading() ) { - return; - } - - // templatelinks table may have become out of sync, - // especially if using variable-based transclusions. - // For paranoia, check if things have changed and if - // so apply updates to the database. This will ensure - // that cascaded protections apply as soon as the changes - // are visible. - - # Get templates from templatelinks - $id = $this->getTitle()->getArticleID(); - - $tlTemplates = array(); - - $dbr = wfGetDB( DB_SLAVE ); - $res = $dbr->select( array( 'templatelinks' ), - array( 'tl_namespace', 'tl_title' ), - array( 'tl_from' => $id ), - __METHOD__ - ); - - foreach ( $res as $row ) { - $tlTemplates["{$row->tl_namespace}:{$row->tl_title}"] = true; - } - - # Get templates from parser output. - $poTemplates = array(); - foreach ( $parserOutput->getTemplates() as $ns => $templates ) { - foreach ( $templates as $dbk => $id ) { - $poTemplates["$ns:$dbk"] = true; - } - } - - # Get the diff - $templates_diff = array_diff_key( $poTemplates, $tlTemplates ); - - if ( count( $templates_diff ) > 0 ) { - # Whee, link updates time. - $u = new LinksUpdate( $this->getTitle(), $parserOutput, false ); - $u->doUpdate(); - } + * User interface handler for the "unwatch" action. + * Requires Request to pass a token as of 1.18. + * @deprecated since 1.18 + */ + public function unwatch() { + wfDeprecated( __METHOD__, '1.18' ); + Action::factory( 'unwatch', $this )->show(); } /** - * Sets the context this Article is executed in - * - * @param $context RequestContext - * @since 1.18 + * Stop watching a page + * @return bool true on successful unwatch + * @deprecated since 1.18 */ - public function setContext( $context ) { - $this->mContext = $context; + public function doUnwatch() { + global $wgUser; + wfDeprecated( __METHOD__, '1.18' ); + return WatchAction::doUnwatch( $this->getTitle(), $wgUser ); } /** - * Gets the context this Article is executed in + * Output a redirect back to the article. + * This is typically used after an edit. * - * @return RequestContext - * @since 1.18 + * @deprecated in 1.18; call $wgOut->redirect() directly + * @param $noRedir Boolean: add redirect=no + * @param $sectionAnchor String: section to redirect to, including "#" + * @param $extraQuery String: extra query params */ - public function getContext() { - if ( $this->mContext instanceof RequestContext ) { - return $this->mContext; + public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) { + wfDeprecated( __METHOD__, '1.18' ); + global $wgOut; + + if ( $noRedir ) { + $query = 'redirect=no'; + if ( $extraQuery ) + $query .= "&$extraQuery"; } else { - wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" ); - return RequestContext::getMain(); + $query = $extraQuery; } + + $wgOut->redirect( $this->getTitle()->getFullURL( $query ) . $sectionAnchor ); } /** @@ -2113,7 +1757,7 @@ class Article extends Page { #wfWarn( "Access to raw $fname field " . __CLASS__ ); return $this->mPage->$fname; } - trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE ); + trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE ); } /** @@ -2122,7 +1766,6 @@ class Article extends Page { * * @param $fname String Field name * @param $fvalue mixed New value - * @param $args Array Arguments to the method */ public function __set( $fname, $fvalue ) { if ( property_exists( $this->mPage, $fname ) ) { @@ -2132,7 +1775,7 @@ class Article extends Page { } elseif ( !in_array( $fname, array( 'mContext', 'mPage' ) ) ) { $this->mPage->$fname = $fvalue; } else { - trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE ); + trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE ); } } @@ -2142,107 +1785,128 @@ class Article extends Page { * * @param $fname String Name of called method * @param $args Array Arguments to the method + * @return mixed */ public function __call( $fname, $args ) { if ( is_callable( array( $this->mPage, $fname ) ) ) { #wfWarn( "Call to " . __CLASS__ . "::$fname; please use WikiPage instead" ); return call_user_func_array( array( $this->mPage, $fname ), $args ); } - trigger_error( 'Inaccessible function via __call(): ' . $fname, E_USER_ERROR ); + trigger_error( 'Inaccessible function via __call(): ' . $fname, E_USER_ERROR ); } // ****** B/C functions to work-around PHP silliness with __call and references ****** // + + /** + * @param $limit array + * @param $expiry array + * @param $cascade bool + * @param $reason string + * @param $user User + * @return Status + */ + public function doUpdateRestrictions( array $limit, array $expiry, &$cascade, $reason, User $user ) { + return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user ); + } + + /** + * @param $limit array + * @param $reason string + * @param $cascade int + * @param $expiry array + * @return bool + */ public function updateRestrictions( $limit = array(), $reason = '', &$cascade = 0, $expiry = array() ) { return $this->mPage->updateRestrictions( $limit, $reason, $cascade, $expiry ); } + /** + * @param $reason string + * @param $suppress bool + * @param $id int + * @param $commit bool + * @param $error string + * @return bool + */ public function doDeleteArticle( $reason, $suppress = false, $id = 0, $commit = true, &$error = '' ) { return $this->mPage->doDeleteArticle( $reason, $suppress, $id, $commit, $error ); } - public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails ) { - return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails ); + /** + * @param $fromP + * @param $summary + * @param $token + * @param $bot + * @param $resultDetails + * @param $user User + * @return array + */ + public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) { + global $wgUser; + $user = is_null( $user ) ? $wgUser : $user; + return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user ); + } + + /** + * @param $fromP + * @param $summary + * @param $bot + * @param $resultDetails + * @param $guser User + * @return array + */ + public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) { + global $wgUser; + $guser = is_null( $guser ) ? $wgUser : $guser; + return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser ); } - public function commitRollback( $fromP, $summary, $bot, &$resultDetails ) { - return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails ); + /** + * @param $hasHistory bool + * @return mixed + */ + public function generateReason( &$hasHistory ) { + return $this->mPage->getAutoDeleteReason( $hasHistory ); } // ****** B/C functions for static methods ( __callStatic is PHP>=5.3 ) ****** // + + /** + * @return array + */ public static function selectFields() { return WikiPage::selectFields(); } + /** + * @param $title Title + */ public static function onArticleCreate( $title ) { - return WikiPage::onArticleCreate( $title ); + WikiPage::onArticleCreate( $title ); } + /** + * @param $title Title + */ public static function onArticleDelete( $title ) { - return WikiPage::onArticleDelete( $title ); - } - - public static function onArticleEdit( $title ) { - return WikiPage::onArticleEdit( $title ); + WikiPage::onArticleDelete( $title ); } - public static function getAutosummary( $oldtext, $newtext, $flags ) { - return WikiPage::getAutosummary( $oldtext, $newtext, $flags ); - } - // ****** -} - -class PoolWorkArticleView extends PoolCounterWork { - /** - * @var Article + * @param $title Title */ - private $mArticle; - - function __construct( $article, $key, $useParserCache, $parserOptions ) { - parent::__construct( 'ArticleView', $key ); - $this->mArticle = $article; - $this->cacheable = $useParserCache; - $this->parserOptions = $parserOptions; - } - - function doWork() { - return $this->mArticle->doViewParse(); - } - - function getCachedWork() { - global $wgOut; - - $parserCache = ParserCache::singleton(); - $this->mArticle->mParserOutput = $parserCache->get( $this->mArticle, $this->parserOptions ); - - if ( $this->mArticle->mParserOutput !== false ) { - wfDebug( __METHOD__ . ": showing contents parsed by someone else\n" ); - $wgOut->addParserOutput( $this->mArticle->mParserOutput ); - # Ensure that UI elements requiring revision ID have - # the correct version information. - $wgOut->setRevisionId( $this->mArticle->getLatest() ); - return true; - } - return false; - } - - function fallback() { - return $this->mArticle->tryDirtyCache(); + public static function onArticleEdit( $title ) { + WikiPage::onArticleEdit( $title ); } /** - * @param $status Status + * @param $oldtext + * @param $newtext + * @param $flags + * @return string */ - function error( $status ) { - global $wgOut; - - $wgOut->clearHTML(); // for release() errors - $wgOut->enableClientCache( false ); - $wgOut->setRobotPolicy( 'noindex,nofollow' ); - - $errortext = $status->getWikiText( false, 'view-pool-error' ); - $wgOut->addWikiText( '<div class="errorbox">' . $errortext . '</div>' ); - - return false; + public static function getAutosummary( $oldtext, $newtext, $flags ) { + return WikiPage::getAutosummary( $oldtext, $newtext, $flags ); } + // ****** }