From: jenkins-bot Date: Wed, 18 Mar 2015 16:56:58 +0000 (+0000) Subject: Merge "Drop outdated "documentation reviewed" tags" X-Git-Tag: 1.31.0-rc.0~12055 X-Git-Url: https://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/banques/?a=commitdiff_plain;h=badc035712ded02e8ec7ee4c5e8a0fe09e2811d2;hp=-c;p=lhc%2Fweb%2Fwiklou.git Merge "Drop outdated "documentation reviewed" tags" --- badc035712ded02e8ec7ee4c5e8a0fe09e2811d2 diff --combined includes/MediaWiki.php index 3b463ae4bd,ba2294dfa0..26440239ab --- a/includes/MediaWiki.php +++ b/includes/MediaWiki.php @@@ -22,8 -22,6 +22,6 @@@ /** * The MediaWiki class is the helper class for the index.php entry point. - * - * @internal documentation reviewed 15 Mar 2010 */ class MediaWiki { /** @@@ -59,7 -57,7 +57,7 @@@ $request = $this->context->getRequest(); $curid = $request->getInt( 'curid' ); $title = $request->getVal( 'title' ); - $action = $request->getVal( 'action', 'view' ); + $action = $request->getVal( 'action' ); if ( $request->getCheck( 'search' ) ) { // Compatibility with old search URLs which didn't use Special:Search @@@ -157,6 -155,8 +155,6 @@@ private function performRequest() { global $wgTitle; - wfProfileIn( __METHOD__ ); - $request = $this->context->getRequest(); $requestTitle = $title = $this->context->getTitle(); $output = $this->context->getOutput(); @@@ -174,6 -174,7 +172,6 @@@ || $title->isSpecial( 'Badtitle' ) ) { $this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) ); - wfProfileOut( __METHOD__ ); throw new BadTitleError(); } @@@ -198,6 -199,7 +196,6 @@@ $this->context->setTitle( $badTitle ); $wgTitle = $badTitle; - wfProfileOut( __METHOD__ ); throw new PermissionsError( 'read', $permErrors ); } @@@ -221,6 -223,7 +219,6 @@@ $output->redirect( $url, 301 ); } else { $this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) ); - wfProfileOut( __METHOD__ ); throw new BadTitleError(); } // Redirect loops, no title in URL, $wgUsePathInfo URLs, and URLs with a variant @@@ -278,6 -281,7 +276,6 @@@ } elseif ( is_string( $article ) ) { $output->redirect( $article ); } else { - wfProfileOut( __METHOD__ ); throw new MWException( "Shouldn't happen: MediaWiki::initializeArticle()" . " returned neither an object nor a URL" ); } @@@ -288,6 -292,7 +286,6 @@@ $user->addAutopromoteOnceGroups( 'onView' ); } - wfProfileOut( __METHOD__ ); } /** @@@ -297,6 -302,7 +295,6 @@@ * @return mixed An Article, or a string to redirect to another URL */ private function initializeArticle() { - wfProfileIn( __METHOD__ ); $title = $this->context->getTitle(); if ( $this->context->canUseWikiPage() ) { @@@ -314,6 -320,7 +312,6 @@@ // NS_MEDIAWIKI has no redirects. // It is also used for CSS/JS, so performance matters here... if ( $title->getNamespace() == NS_MEDIAWIKI ) { - wfProfileOut( __METHOD__ ); return $article; } @@@ -344,6 -351,7 +342,6 @@@ if ( is_string( $target ) ) { if ( !$this->config->get( 'DisableHardRedirects' ) ) { // we'll need to redirect - wfProfileOut( __METHOD__ ); return $target; } } @@@ -364,6 -372,7 +362,6 @@@ } } - wfProfileOut( __METHOD__ ); return $article; } @@@ -374,6 -383,7 +372,6 @@@ * @param Title $requestTitle The original title, before any redirects were applied */ private function performAction( Page $page, Title $requestTitle ) { - wfProfileIn( __METHOD__ ); $request = $this->context->getRequest(); $output = $this->context->getOutput(); @@@ -383,6 -393,7 +381,6 @@@ if ( !Hooks::run( 'MediaWikiPerformAction', array( $output, $page, $title, $user, $request, $this ) ) ) { - wfProfileOut( __METHOD__ ); return; } @@@ -393,16 -404,13 +391,16 @@@ if ( $action instanceof Action ) { # Let Squid cache things if we can purge them. if ( $this->config->get( 'UseSquid' ) && - in_array( $request->getFullRequestURL(), $requestTitle->getSquidURLs() ) + in_array( + // Use PROTO_INTERNAL because that's what getSquidURLs() uses + wfExpandUrl( $request->getRequestURL(), PROTO_INTERNAL ), + $requestTitle->getSquidURLs() + ) ) { $output->setSquidMaxage( $this->config->get( 'SquidMaxage' ) ); } $action->show(); - wfProfileOut( __METHOD__ ); return; } @@@ -411,6 -419,7 +409,6 @@@ $output->showErrorPage( 'nosuchaction', 'nosuchactiontext' ); } - wfProfileOut( __METHOD__ ); } /** @@@ -445,6 -454,7 +443,6 @@@ * @return bool */ private function checkMaxLag() { - wfProfileIn( __METHOD__ ); $maxLag = $this->context->getRequest()->getVal( 'maxlag' ); if ( !is_null( $maxLag ) ) { list( $host, $lag ) = wfGetLB()->getMaxLag(); @@@ -460,19 -470,25 +458,19 @@@ echo "Waiting for a database server: $lag seconds lagged\n"; } - wfProfileOut( __METHOD__ ); - exit; } } - wfProfileOut( __METHOD__ ); return true; } private function main() { global $wgTitle; - wfProfileIn( __METHOD__ ); - $request = $this->context->getRequest(); // Send Ajax requests to the Ajax dispatcher. - if ( $this->config->get( 'UseAjax' ) && $request->getVal( 'action', 'view' ) == 'ajax' ) { - + if ( $this->config->get( 'UseAjax' ) && $request->getVal( 'action' ) === 'ajax' ) { // Set a dummy title, because $wgTitle == null might break things $title = Title::makeTitle( NS_MAIN, 'AJAX' ); $this->context->setTitle( $title ); @@@ -480,6 -496,7 +478,6 @@@ $dispatcher = new AjaxDispatcher( $this->config ); $dispatcher->performAction( $this->context->getUser() ); - wfProfileOut( __METHOD__ ); return; } @@@ -489,19 -506,6 +487,19 @@@ $action = $this->getAction(); $wgTitle = $title; + $trxProfiler = Profiler::instance()->getTransactionProfiler(); + + // Aside from rollback, master queries should not happen on GET requests. + // Periodic or "in passing" updates on GET should use the job queue. + if ( !$request->wasPosted() + && in_array( $action, array( 'view', 'edit', 'history' ) ) + ) { + $trxProfiler->setExpectation( 'masterConns', 0, __METHOD__ ); + $trxProfiler->setExpectation( 'writes', 0, __METHOD__ ); + } else { + $trxProfiler->setExpectation( 'maxAffected', 500, __METHOD__ ); + } + // If the user has forceHTTPS set to true, or if the user // is in a group requiring HTTPS, or if they have the HTTPS // preference set, redirect them to HTTPS. @@@ -545,11 -549,13 +543,11 @@@ $output->addVaryHeader( 'X-Forwarded-Proto' ); $output->redirect( $redirUrl ); $output->output(); - wfProfileOut( __METHOD__ ); return; } } if ( $this->config->get( 'UseFileCache' ) && $title->getNamespace() >= 0 ) { - wfProfileIn( 'main-try-filecache' ); if ( HTMLFileCache::useFileCache( $this->context ) ) { // Try low-level file cache hit $cache = new HTMLFileCache( $title, $action ); @@@ -564,9 -570,12 +562,9 @@@ $this->context->getWikiPage()->doViewUpdates( $this->context->getUser() ); // Tell OutputPage that output is taken care of $this->context->getOutput()->disable(); - wfProfileOut( 'main-try-filecache' ); - wfProfileOut( __METHOD__ ); return; } } - wfProfileOut( 'main-try-filecache' ); } // Actually do the work of the request and build up any output @@@ -582,16 -591,13 +580,16 @@@ // Output everything! $this->context->getOutput()->output(); - wfProfileOut( __METHOD__ ); } /** * Ends this task peacefully */ public function restInPeace() { + // Ignore things like master queries/connections on GET requests + // as long as they are in deferred updates (which catch errors). + Profiler::instance()->getTransactionProfiler()->resetExpectations(); + // Do any deferred jobs DeferredUpdates::doUpdates( 'commit' ); @@@ -619,6 -625,8 +617,6 @@@ return; // recursion guard } - $section = new ProfileSection( __METHOD__ ); - if ( $jobRunRate < 1 ) { $max = mt_getrandmax(); if ( mt_rand( 0, $max ) > $max * $jobRunRate ) { @@@ -629,11 -637,9 +627,11 @@@ $n = intval( $jobRunRate ); } + $runJobsLogger = MWLoggerFactory::getInstance( 'runJobs' ); + if ( !$this->config->get( 'RunJobsAsync' ) ) { // Fall back to running the job here while the user waits - $runner = new JobRunner(); + $runner = new JobRunner( $runJobsLogger ); $runner->run( array( 'maxJobs' => $n ) ); return; } @@@ -666,9 -672,9 +664,9 @@@ ); wfRestoreWarnings(); if ( !$sock ) { - wfDebugLog( 'runJobs', "Failed to start cron API (socket error $errno): $errstr\n" ); + $runJobsLogger->error( "Failed to start cron API (socket error $errno): $errstr" ); // Fall back to running the job here while the user waits - $runner = new JobRunner(); + $runner = new JobRunner( $runJobsLogger ); $runner->run( array( 'maxJobs' => $n ) ); return; } @@@ -676,19 -682,19 +674,19 @@@ $url = wfAppendQuery( wfScript( 'index' ), $query ); $req = "POST $url HTTP/1.1\r\nHost: {$info['host']}\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n"; - wfDebugLog( 'runJobs', "Running $n job(s) via '$url'\n" ); + $runJobsLogger->info( "Running $n job(s) via '$url'" ); // Send a cron API request to be performed in the background. // Give up if this takes too long to send (which should be rare). stream_set_timeout( $sock, 1 ); $bytes = fwrite( $sock, $req ); if ( $bytes !== strlen( $req ) ) { - wfDebugLog( 'runJobs', "Failed to start cron API (socket write error)\n" ); + $runJobsLogger->error( "Failed to start cron API (socket write error)" ); } else { // Do not wait for the response (the script should handle client aborts). // Make sure that we don't close before that script reaches ignore_user_abort(). $status = fgets( $sock ); if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) { - wfDebugLog( 'runJobs', "Failed to start cron API: received '$status'\n" ); + $runJobsLogger->error( "Failed to start cron API: received '$status'" ); } } fclose( $sock ); diff --combined includes/Title.php index f47300cd03,ff85197b92..921538b287 --- a/includes/Title.php +++ b/includes/Title.php @@@ -29,8 -29,6 +29,6 @@@ * however, it does so inefficiently. * @note Consider using a TitleValue object instead. TitleValue is more lightweight * and does not rely on global state or the database. - * - * @internal documentation reviewed 15 Mar 2010 */ class Title { /** @var MapCacheLRU */ @@@ -614,13 -612,28 +612,13 @@@ * Note that this doesn't pick up many things that could be wrong with titles, but that * replacing this regex with something valid will make many titles valid. * - * @todo move this into MediaWikiTitleCodec + * @deprecated since 1.25, use MediaWikiTitleCodec::getTitleInvalidRegex() instead * * @return string Regex string */ static function getTitleInvalidRegex() { - static $rxTc = false; - if ( !$rxTc ) { - # Matching titles will be held as illegal. - $rxTc = '/' . - # Any character not allowed is forbidden... - '[^' . self::legalChars() . ']' . - # URL percent encoding sequences interfere with the ability - # to round-trip titles -- you can't link to them consistently. - '|%[0-9A-Fa-f]{2}' . - # XML/HTML character references produce similar issues. - '|&[A-Za-z0-9\x80-\xff]+;' . - '|&#[0-9]+;' . - '|&#x[0-9A-Fa-f]+;' . - '/S'; - } - - return $rxTc; + wfDeprecated( __METHOD__, '1.25' ); + return MediaWikiTitleCodec::getTitleInvalidRegex(); } /** @@@ -942,9 -955,9 +940,9 @@@ * @return string Content model id */ public function getContentModel( $flags = 0 ) { - # Calling getArticleID() loads the field from cache as needed if ( !$this->mContentModel && $this->getArticleID( $flags ) ) { $linkCache = LinkCache::singleton(); + $linkCache->addLinkObj( $this ); # in case we already had an article ID $this->mContentModel = $linkCache->getGoodLinkFieldObj( $this, 'model' ); } @@@ -1319,25 -1332,6 +1317,25 @@@ return Title::makeTitle( $subjectNS, $this->getDBkey() ); } + /** + * Get the other title for this page, if this is a subject page + * get the talk page, if it is a subject page get the talk page + * + * @since 1.25 + * @throws MWException + * @return Title + */ + public function getOtherPage() { + if ( $this->isSpecialPage() ) { + throw new MWException( 'Special pages cannot have other pages' ); + } + if ( $this->isTalkPage() ) { + return $this->getSubjectPage(); + } else { + return $this->getTalkPage(); + } + } + /** * Get the default namespace index, for when there is no namespace * @@@ -1782,6 -1776,7 +1780,6 @@@ * @return string The URL */ public function getLinkURL( $query = '', $query2 = false, $proto = PROTO_RELATIVE ) { - wfProfileIn( __METHOD__ ); if ( $this->isExternal() || $proto !== PROTO_RELATIVE ) { $ret = $this->getFullURL( $query, $query2, $proto ); } elseif ( $this->getPrefixedText() === '' && $this->hasFragment() ) { @@@ -1789,6 -1784,7 +1787,6 @@@ } else { $ret = $this->getLocalURL( $query, $query2 ) . $this->getFragmentForURL(); } - wfProfileOut( __METHOD__ ); return $ret; } @@@ -1888,16 -1884,18 +1886,16 @@@ * @param string $action Action that permission needs to be checked for * @param User $user User to check (since 1.19); $wgUser will be used if not * provided. - * @param bool $doExpensiveQueries Set this to false to avoid doing - * unnecessary queries. + * @param string $rigor Same format as Title::getUserPermissionsErrors() * @return bool */ - public function userCan( $action, $user = null, $doExpensiveQueries = true ) { + public function userCan( $action, $user = null, $rigor = 'secure' ) { if ( !$user instanceof User ) { global $wgUser; $user = $wgUser; } - return !count( $this->getUserPermissionsErrorsInternal( - $action, $user, $doExpensiveQueries, true ) ); + return !count( $this->getUserPermissionsErrorsInternal( $action, $user, $rigor, true ) ); } /** @@@ -1907,19 -1905,16 +1905,19 @@@ * * @param string $action Action that permission needs to be checked for * @param User $user User to check - * @param bool $doExpensiveQueries Set this to false to avoid doing unnecessary - * queries by skipping checks for cascading protections and user blocks. + * @param string $rigor One of (quick,full,secure) + * - quick : does cheap permission checks from slaves (usable for GUI creation) + * - full : does cheap and expensive checks possibly from a slave + * - secure : does cheap and expensive checks, using the master as needed + * @param bool $short Set this to true to stop after the first permission error. * @param array $ignoreErrors Array of Strings Set this to a list of message keys * whose corresponding errors may be ignored. * @return array Array of arguments to wfMessage to explain permissions problems. */ - public function getUserPermissionsErrors( $action, $user, $doExpensiveQueries = true, - $ignoreErrors = array() + public function getUserPermissionsErrors( + $action, $user, $rigor = 'secure', $ignoreErrors = array() ) { - $errors = $this->getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries ); + $errors = $this->getUserPermissionsErrorsInternal( $action, $user, $rigor ); // Remove the errors being ignored. foreach ( $errors as $index => $error ) { @@@ -1939,14 -1934,16 +1937,14 @@@ * @param string $action The action to check * @param User $user User to check * @param array $errors List of current errors - * @param bool $doExpensiveQueries Whether or not to perform expensive queries + * @param string $rigor Same format as Title::getUserPermissionsErrors() * @param bool $short Short circuit on first error * * @return array List of errors */ - private function checkQuickPermissions( $action, $user, $errors, - $doExpensiveQueries, $short - ) { + private function checkQuickPermissions( $action, $user, $errors, $rigor, $short ) { if ( !Hooks::run( 'TitleQuickPermissions', - array( $this, $user, $action, &$errors, $doExpensiveQueries, $short ) ) + array( $this, $user, $action, &$errors, ( $rigor !== 'quick' ), $short ) ) ) { return $errors; } @@@ -2037,12 -2034,12 +2035,12 @@@ * @param string $action The action to check * @param User $user User to check * @param array $errors List of current errors - * @param bool $doExpensiveQueries Whether or not to perform expensive queries + * @param string $rigor Same format as Title::getUserPermissionsErrors() * @param bool $short Short circuit on first error * * @return array List of errors */ - private function checkPermissionHooks( $action, $user, $errors, $doExpensiveQueries, $short ) { + private function checkPermissionHooks( $action, $user, $errors, $rigor, $short ) { // Use getUserPermissionsErrors instead $result = ''; if ( !Hooks::run( 'userCan', array( &$this, &$user, $action, &$result ) ) ) { @@@ -2054,7 -2051,7 +2052,7 @@@ } // Check getUserPermissionsErrorsExpensive hook if ( - $doExpensiveQueries + $rigor !== 'quick' && !( $short && count( $errors ) > 0 ) && !Hooks::run( 'getUserPermissionsErrorsExpensive', array( &$this, &$user, $action, &$result ) ) ) { @@@ -2070,12 -2067,14 +2068,12 @@@ * @param string $action The action to check * @param User $user User to check * @param array $errors List of current errors - * @param bool $doExpensiveQueries Whether or not to perform expensive queries + * @param string $rigor Same format as Title::getUserPermissionsErrors() * @param bool $short Short circuit on first error * * @return array List of errors */ - private function checkSpecialsAndNSPermissions( $action, $user, $errors, - $doExpensiveQueries, $short - ) { + private function checkSpecialsAndNSPermissions( $action, $user, $errors, $rigor, $short ) { # Only 'createaccount' can be performed on special pages, # which don't actually exist in the DB. if ( NS_SPECIAL == $this->mNamespace && $action !== 'createaccount' ) { @@@ -2099,12 -2098,12 +2097,12 @@@ * @param string $action The action to check * @param User $user User to check * @param array $errors List of current errors - * @param bool $doExpensiveQueries Whether or not to perform expensive queries + * @param string $rigor Same format as Title::getUserPermissionsErrors() * @param bool $short Short circuit on first error * * @return array List of errors */ - private function checkCSSandJSPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) { + private function checkCSSandJSPermissions( $action, $user, $errors, $rigor, $short ) { # Protect css/js subpages of user pages # XXX: this might be better using restrictions # XXX: right 'editusercssjs' is deprecated, for backward compatibility only @@@ -2135,12 -2134,12 +2133,12 @@@ * @param string $action The action to check * @param User $user User to check * @param array $errors List of current errors - * @param bool $doExpensiveQueries Whether or not to perform expensive queries + * @param string $rigor Same format as Title::getUserPermissionsErrors() * @param bool $short Short circuit on first error * * @return array List of errors */ - private function checkPageRestrictions( $action, $user, $errors, $doExpensiveQueries, $short ) { + private function checkPageRestrictions( $action, $user, $errors, $rigor, $short ) { foreach ( $this->getRestrictions( $action ) as $right ) { // Backwards compatibility, rewrite sysop -> editprotected if ( $right == 'sysop' ) { @@@ -2169,13 -2168,15 +2167,13 @@@ * @param string $action The action to check * @param User $user User to check * @param array $errors List of current errors - * @param bool $doExpensiveQueries Whether or not to perform expensive queries + * @param string $rigor Same format as Title::getUserPermissionsErrors() * @param bool $short Short circuit on first error * * @return array List of errors */ - private function checkCascadingSourcesRestrictions( $action, $user, $errors, - $doExpensiveQueries, $short - ) { - if ( $doExpensiveQueries && !$this->isCssJsSubpage() ) { + private function checkCascadingSourcesRestrictions( $action, $user, $errors, $rigor, $short ) { + if ( $rigor !== 'quick' && !$this->isCssJsSubpage() ) { # We /could/ use the protection level on the source page, but it's # fairly ugly as we have to establish a precedence hierarchy for pages # included by multiple cascade-protected pages. So just restrict @@@ -2216,16 -2217,20 +2214,16 @@@ * @param string $action The action to check * @param User $user User to check * @param array $errors List of current errors - * @param bool $doExpensiveQueries Whether or not to perform expensive queries + * @param string $rigor Same format as Title::getUserPermissionsErrors() * @param bool $short Short circuit on first error * * @return array List of errors */ - private function checkActionPermissions( $action, $user, $errors, - $doExpensiveQueries, $short - ) { + private function checkActionPermissions( $action, $user, $errors, $rigor, $short ) { global $wgDeleteRevisionsLimit, $wgLang; if ( $action == 'protect' ) { - if ( count( $this->getUserPermissionsErrorsInternal( 'edit', - $user, $doExpensiveQueries, true ) ) - ) { + if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $rigor, true ) ) ) { // If they can't edit, they shouldn't protect. $errors[] = array( 'protect-cantedit' ); } @@@ -2258,16 -2263,17 +2256,16 @@@ $errors[] = array( 'immobile-target-page' ); } } elseif ( $action == 'delete' ) { - $tempErrors = $this->checkPageRestrictions( 'edit', - $user, array(), $doExpensiveQueries, true ); + $tempErrors = $this->checkPageRestrictions( 'edit', $user, array(), $rigor, true ); if ( !$tempErrors ) { $tempErrors = $this->checkCascadingSourcesRestrictions( 'edit', - $user, $tempErrors, $doExpensiveQueries, true ); + $user, $tempErrors, $rigor, true ); } if ( $tempErrors ) { // If protection keeps them from editing, they shouldn't be able to delete. $errors[] = array( 'deleteprotected' ); } - if ( $doExpensiveQueries && $wgDeleteRevisionsLimit + if ( $rigor !== 'quick' && $wgDeleteRevisionsLimit && !$this->userCan( 'bigdelete', $user ) && $this->isBigDeletion() ) { $errors[] = array( 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ); @@@ -2282,15 -2288,15 +2280,15 @@@ * @param string $action The action to check * @param User $user User to check * @param array $errors List of current errors - * @param bool $doExpensiveQueries Whether or not to perform expensive queries + * @param string $rigor Same format as Title::getUserPermissionsErrors() * @param bool $short Short circuit on first error * * @return array List of errors */ - private function checkUserBlock( $action, $user, $errors, $doExpensiveQueries, $short ) { + private function checkUserBlock( $action, $user, $errors, $rigor, $short ) { // Account creation blocks handled at userlogin. // Unblocking handled in SpecialUnblock - if ( !$doExpensiveQueries || in_array( $action, array( 'createaccount', 'unblock' ) ) ) { + if ( $rigor === 'quick' || in_array( $action, array( 'createaccount', 'unblock' ) ) ) { return $errors; } @@@ -2300,13 -2306,10 +2298,13 @@@ $errors[] = array( 'confirmedittext' ); } - if ( ( $action == 'edit' || $action == 'create' ) && !$user->isBlockedFrom( $this ) ) { + $useSlave = ( $rigor !== 'secure' ); + if ( ( $action == 'edit' || $action == 'create' ) + && !$user->isBlockedFrom( $this, $useSlave ) + ) { // Don't block the user from editing their own talk page unless they've been // explicitly blocked from that too. - } elseif ( $user->isBlocked() && $user->mBlock->prevents( $action ) !== false ) { + } elseif ( $user->isBlocked() && $user->getBlock()->prevents( $action ) !== false ) { // @todo FIXME: Pass the relevant context into this function. $errors[] = $user->getBlock()->getPermissionsError( RequestContext::getMain() ); } @@@ -2320,12 -2323,12 +2318,12 @@@ * @param string $action The action to check * @param User $user User to check * @param array $errors List of current errors - * @param bool $doExpensiveQueries Whether or not to perform expensive queries + * @param string $rigor Same format as Title::getUserPermissionsErrors() * @param bool $short Short circuit on first error * * @return array List of errors */ - private function checkReadPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) { + private function checkReadPermissions( $action, $user, $errors, $rigor, $short ) { global $wgWhitelistRead, $wgWhitelistReadRegexp; $whitelisted = false; @@@ -2428,23 -2431,14 +2426,23 @@@ * * @param string $action Action that permission needs to be checked for * @param User $user User to check - * @param bool $doExpensiveQueries Set this to false to avoid doing unnecessary queries. + * @param string $rigor One of (quick,full,secure) + * - quick : does cheap permission checks from slaves (usable for GUI creation) + * - full : does cheap and expensive checks possibly from a slave + * - secure : does cheap and expensive checks, using the master as needed * @param bool $short Set this to true to stop after the first permission error. * @return array Array of arrays of the arguments to wfMessage to explain permissions problems. */ - protected function getUserPermissionsErrorsInternal( $action, $user, - $doExpensiveQueries = true, $short = false + protected function getUserPermissionsErrorsInternal( + $action, $user, $rigor = 'secure', $short = false ) { - wfProfileIn( __METHOD__ ); + if ( $rigor === true ) { + $rigor = 'secure'; // b/c + } elseif ( $rigor === false ) { + $rigor = 'quick'; // b/c + } elseif ( !in_array( $rigor, array( 'quick', 'full', 'secure' ) ) ) { + throw new Exception( "Invalid rigor parameter '$rigor'." ); + } # Read has special handling if ( $action == 'read' ) { @@@ -2482,9 -2476,10 +2480,9 @@@ while ( count( $checks ) > 0 && !( $short && count( $errors ) > 0 ) ) { $method = array_shift( $checks ); - $errors = $this->$method( $action, $user, $errors, $doExpensiveQueries, $short ); + $errors = $this->$method( $action, $user, $errors, $rigor, $short ); } - wfProfileOut( __METHOD__ ); return $errors; } @@@ -2720,6 -2715,8 +2718,6 @@@ return array( $this->mHasCascadingRestrictions, $pagerestrictions ); } - wfProfileIn( __METHOD__ ); - $dbr = wfGetDB( DB_SLAVE ); if ( $this->getNamespace() == NS_FILE ) { @@@ -2752,6 -2749,7 +2750,6 @@@ $sources = $getPages ? array() : false; $now = wfTimestampNow(); - $purgeExpired = false; foreach ( $res as $row ) { $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW ); @@@ -2777,8 -2775,14 +2775,8 @@@ } else { $sources = true; } - } else { - // Trigger lazy purge of expired restrictions from the db - $purgeExpired = true; } } - if ( $purgeExpired ) { - Title::purgeExpiredRestrictions(); - } if ( $getPages ) { $this->mCascadeSources = $sources; @@@ -2787,6 -2791,7 +2785,6 @@@ $this->mHasCascadingRestrictions = $sources; } - wfProfileOut( __METHOD__ ); return array( $sources, $pagerestrictions ); } @@@ -2929,6 -2934,7 +2927,6 @@@ if ( count( $rows ) ) { # Current system - load second to make them override. $now = wfTimestampNow(); - $purgeExpired = false; # Cycle through all the restrictions. foreach ( $rows as $row ) { @@@ -2948,8 -2954,15 +2946,8 @@@ $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) ); $this->mCascadeRestriction |= $row->pr_cascade; - } else { - // Trigger a lazy purge of expired restrictions - $purgeExpired = true; } } - - if ( $purgeExpired ) { - Title::purgeExpiredRestrictions(); - } } $this->mRestrictionsLoaded = true; @@@ -2987,6 -3000,7 +2985,6 @@@ $this->mRestrictionsExpiry['create'] = $expiry; $this->mRestrictions['create'] = explode( ',', trim( $title_protection['permission'] ) ); } else { // Get rid of the old restrictions - Title::purgeExpiredRestrictions(); $this->mTitleProtection = false; } } else { @@@ -3172,13 -3186,13 +3170,13 @@@ if ( !is_null( $this->mRedirect ) ) { return $this->mRedirect; } - # Calling getArticleID() loads the field from cache as needed if ( !$this->getArticleID( $flags ) ) { $this->mRedirect = false; return $this->mRedirect; } $linkCache = LinkCache::singleton(); + $linkCache->addLinkObj( $this ); # in case we already had an article ID $cached = $linkCache->getGoodLinkFieldObj( $this, 'redirect' ); if ( $cached === null ) { # Trust LinkCache's state over our own @@@ -3207,12 -3221,12 +3205,12 @@@ if ( $this->mLength != -1 ) { return $this->mLength; } - # Calling getArticleID() loads the field from cache as needed if ( !$this->getArticleID( $flags ) ) { $this->mLength = 0; return $this->mLength; } $linkCache = LinkCache::singleton(); + $linkCache->addLinkObj( $this ); # in case we already had an article ID $cached = $linkCache->getGoodLinkFieldObj( $this, 'length' ); if ( $cached === null ) { # Trust LinkCache's state over our own, as for isRedirect() @@@ -3235,12 -3249,13 +3233,12 @@@ if ( !( $flags & Title::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) { return intval( $this->mLatestID ); } - # Calling getArticleID() loads the field from cache as needed if ( !$this->getArticleID( $flags ) ) { $this->mLatestID = 0; return $this->mLatestID; } $linkCache = LinkCache::singleton(); - $linkCache->addLinkObj( $this ); + $linkCache->addLinkObj( $this ); # in case we already had an article ID $cached = $linkCache->getGoodLinkFieldObj( $this, 'revision' ); if ( $cached === null ) { # Trust LinkCache's state over our own, as for isRedirect() @@@ -3594,7 -3609,7 +3592,7 @@@ * * @deprecated since 1.25, use MovePage's methods instead * @param Title $nt The new title - * @param bool $auth Ignored + * @param bool $auth Whether to check user permissions (uses $wgUser) * @param string $reason Is the log summary of the move, used for spam checking * @return array|bool True on success, getUserPermissionsErrors()-like array on failure */ @@@ -3608,13 -3623,10 +3606,13 @@@ } $mp = new MovePage( $this, $nt ); - $errors = wfMergeErrorArrays( - $mp->isValidMove()->getErrorsArray(), - $mp->checkPermissions( $wgUser, $reason )->getErrorsArray() - ); + $errors = $mp->isValidMove()->getErrorsArray(); + if ( $auth ) { + $errors = wfMergeErrorArrays( + $errors, + $mp->checkPermissions( $wgUser, $reason )->getErrorsArray() + ); + } return $errors ? : true; } @@@ -3631,10 -3643,7 +3629,10 @@@ $errors = array(); $destFile = wfLocalFile( $nt ); - if ( !$wgUser->isAllowed( 'reupload-shared' ) && !$destFile->exists() && wfFindFile( $nt ) ) { + $destFile->load( File::READ_LATEST ); + if ( !$wgUser->isAllowed( 'reupload-shared' ) + && !$destFile->exists() && wfFindFile( $nt ) + ) { $errors[] = array( 'file-exists-sharedrepo' ); } @@@ -3809,7 -3818,6 +3807,7 @@@ # Is it an existing file? if ( $nt->getNamespace() == NS_FILE ) { $file = wfLocalFile( $nt ); + $file->load( File::READ_LATEST ); if ( $file->exists() ) { wfDebug( __METHOD__ . ": file exists\n" ); return false; @@@ -4032,7 -4040,7 +4030,7 @@@ if ( $this->mIsBigDeletion === null ) { $dbr = wfGetDB( DB_SLAVE ); - $innerQuery = $dbr->selectSQLText( + $revCount = $dbr->selectRowCount( 'revision', '1', array( 'rev_page' => $this->getArticleID() ), @@@ -4040,6 -4048,13 +4038,6 @@@ array( 'LIMIT' => $wgDeleteRevisionsLimit + 1 ) ); - $revCount = $dbr->query( - 'SELECT COUNT(*) FROM (' . $innerQuery . ') AS innerQuery', - __METHOD__ - ); - $revCount = $revCount->fetchRow(); - $revCount = $revCount['COUNT(*)']; - $this->mIsBigDeletion = $revCount > $wgDeleteRevisionsLimit; } @@@ -4091,11 -4106,12 +4089,11 @@@ 'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) ) ); if ( $max !== null ) { - $res = $dbr->select( 'revision', '1', + return $dbr->selectRowCount( 'revision', '1', $conds, __METHOD__, array( 'LIMIT' => $max + 1 ) // extra to detect truncation ); - return $res->numRows(); } else { return (int)$dbr->selectField( 'revision', 'count(*)', $conds, __METHOD__ ); } @@@ -4146,19 -4162,17 +4144,19 @@@ } // No DB query needed if $old and $new are the same or successive revisions: if ( $old->getId() === $new->getId() ) { - return ( $old_cmp === '>' && $new_cmp === '<' ) ? array() : array( $old->getRawUserText() ); + return ( $old_cmp === '>' && $new_cmp === '<' ) ? + array() : + array( $old->getUserText( Revision::RAW ) ); } elseif ( $old->getId() === $new->getParentId() ) { if ( $old_cmp === '>=' && $new_cmp === '<=' ) { - $authors[] = $old->getRawUserText(); - if ( $old->getRawUserText() != $new->getRawUserText() ) { - $authors[] = $new->getRawUserText(); + $authors[] = $old->getUserText( Revision::RAW ); + if ( $old->getUserText( Revision::RAW ) != $new->getUserText( Revision::RAW ) ) { + $authors[] = $new->getUserText( Revision::RAW ); } } elseif ( $old_cmp === '>=' ) { - $authors[] = $old->getRawUserText(); + $authors[] = $old->getUserText( Revision::RAW ); } elseif ( $new_cmp === '<=' ) { - $authors[] = $new->getRawUserText(); + $authors[] = $new->getUserText( Revision::RAW ); } return $authors; } @@@ -4610,13 -4624,16 +4608,13 @@@ */ public function getPageLanguage() { global $wgLang, $wgLanguageCode; - wfProfileIn( __METHOD__ ); if ( $this->isSpecialPage() ) { // special pages are in the user language - wfProfileOut( __METHOD__ ); return $wgLang; } // Checking if DB language is set if ( $this->mDbPageLanguage ) { - wfProfileOut( __METHOD__ ); return wfGetLangObj( $this->mDbPageLanguage ); } @@@ -4634,6 -4651,7 +4632,6 @@@ $langObj = wfGetLangObj( $this->mPageLanguage[0] ); } - wfProfileOut( __METHOD__ ); return $langObj; } @@@ -4680,64 -4698,28 +4678,64 @@@ public function getEditNotices( $oldid = 0 ) { $notices = array(); - # Optional notices on a per-namespace and per-page basis + // Optional notice for the entire namespace $editnotice_ns = 'editnotice-' . $this->getNamespace(); - $editnotice_ns_message = wfMessage( $editnotice_ns ); - if ( $editnotice_ns_message->exists() ) { - $notices[$editnotice_ns] = $editnotice_ns_message->parseAsBlock(); + $msg = wfMessage( $editnotice_ns ); + if ( $msg->exists() ) { + $html = $msg->parseAsBlock(); + // Edit notices may have complex logic, but output nothing (T91715) + if ( trim( $html ) !== '' ) { + $notices[$editnotice_ns] = Html::rawElement( + 'div', + array( 'class' => array( + 'mw-editnotice', + 'mw-editnotice-namespace', + Sanitizer::escapeClass( "mw-$editnotice_ns" ) + ) ), + $html + ); + } } + if ( MWNamespace::hasSubpages( $this->getNamespace() ) ) { + // Optional notice for page itself and any parent page $parts = explode( '/', $this->getDBkey() ); $editnotice_base = $editnotice_ns; while ( count( $parts ) > 0 ) { $editnotice_base .= '-' . array_shift( $parts ); - $editnotice_base_msg = wfMessage( $editnotice_base ); - if ( $editnotice_base_msg->exists() ) { - $notices[$editnotice_base] = $editnotice_base_msg->parseAsBlock(); + $msg = wfMessage( $editnotice_base ); + if ( $msg->exists() ) { + $html = $msg->parseAsBlock(); + if ( trim( $html ) !== '' ) { + $notices[$editnotice_base] = Html::rawElement( + 'div', + array( 'class' => array( + 'mw-editnotice', + 'mw-editnotice-base', + Sanitizer::escapeClass( "mw-$editnotice_base" ) + ) ), + $html + ); + } } } } else { - # Even if there are no subpages in namespace, we still don't want / in MW ns. + // Even if there are no subpages in namespace, we still don't want "/" in MediaWiki message keys $editnoticeText = $editnotice_ns . '-' . str_replace( '/', '-', $this->getDBkey() ); - $editnoticeMsg = wfMessage( $editnoticeText ); - if ( $editnoticeMsg->exists() ) { - $notices[$editnoticeText] = $editnoticeMsg->parseAsBlock(); + $msg = wfMessage( $editnoticeText ); + if ( $msg->exists() ) { + $html = $msg->parseAsBlock(); + if ( trim( $html ) !== '' ) { + $notices[$editnoticeText] = Html::rawElement( + 'div', + array( 'class' => array( + 'mw-editnotice', + 'mw-editnotice-page', + Sanitizer::escapeClass( "mw-$editnoticeText" ) + ) ), + $html + ); + } } } diff --combined includes/cache/BacklinkCache.php index c3f455ec5c,97b7665b08..10b4fb0090 --- a/includes/cache/BacklinkCache.php +++ b/includes/cache/BacklinkCache.php @@@ -38,8 -38,6 +38,6 @@@ * of memory. * * Introduced by r47317 - * - * @internal documentation reviewed on 18 Mar 2011 by hashar */ class BacklinkCache { /** @var ProcessCacheLRU */ @@@ -176,6 -174,7 +174,6 @@@ * @return ResultWrapper */ protected function queryLinks( $table, $startId, $endId, $max, $select = 'all' ) { - wfProfileIn( __METHOD__ ); $fromField = $this->getPrefix( $table ) . '_from'; @@@ -230,6 -229,8 +228,6 @@@ } } - wfProfileOut( __METHOD__ ); - return $res; } @@@ -487,55 -488,4 +485,55 @@@ return array( 'numRows' => $numRows, 'batches' => $batches ); } + + /** + * Get a Title iterator for cascade-protected template/file use backlinks + * + * @return TitleArray + * @since 1.25 + */ + public function getCascadeProtectedLinks() { + $dbr = $this->getDB(); + + // @todo: use UNION without breaking tests that use temp tables + $resSets = array(); + $resSets[] = $dbr->select( + array( 'templatelinks', 'page_restrictions', 'page' ), + array( 'page_namespace', 'page_title', 'page_id' ), + array( + 'tl_namespace' => $this->title->getNamespace(), + 'tl_title' => $this->title->getDBkey(), + 'tl_from = pr_page', + 'pr_cascade' => 1, + 'page_id = tl_from' + ), + __METHOD__, + array( 'DISTINCT' ) + ); + if ( $this->title->getNamespace() == NS_FILE ) { + $resSets[] = $dbr->select( + array( 'imagelinks', 'page_restrictions', 'page' ), + array( 'page_namespace', 'page_title', 'page_id' ), + array( + 'il_to' => $this->title->getDBkey(), + 'il_from = pr_page', + 'pr_cascade' => 1, + 'page_id = il_from' + ), + __METHOD__, + array( 'DISTINCT' ) + ); + } + + // Combine and de-duplicate the results + $mergedRes = array(); + foreach ( $resSets as $res ) { + foreach ( $res as $row ) { + $mergedRes[$row->page_id] = $row; + } + } + + return TitleArray::newFromResult( + new FakeResultWrapper( array_values( $mergedRes ) ) ); + } } diff --combined includes/page/Article.php index 6ebd8a197a,5752262e43..eb597d2924 --- a/includes/page/Article.php +++ b/includes/page/Article.php @@@ -30,8 -30,6 +30,6 @@@ * See design.txt for an overview. * Note: edit user interface and cache support functions have been * moved to separate EditPage and HTMLFileCache classes. - * - * @internal documentation reviewed 15 Mar 2010 */ class Article implements Page { /** @var IContextSource The context this Article is executed in */ @@@ -226,6 -224,7 +224,6 @@@ * @since 1.21 */ protected function getContentObject() { - wfProfileIn( __METHOD__ ); if ( $this->mPage->getID() === 0 ) { # If this is a MediaWiki:x message, then load the messages @@@ -246,6 -245,7 +244,6 @@@ $content = $this->mContentObject; } - wfProfileOut( __METHOD__ ); return $content; } @@@ -342,9 -342,12 +340,9 @@@ return $this->mContent; } - wfProfileIn( __METHOD__ ); - $content = $this->fetchContentObject(); if ( !$content ) { - wfProfileOut( __METHOD__ ); return false; } @@@ -352,6 -355,8 +350,6 @@@ $this->mContent = ContentHandler::getContentText( $content ); ContentHandler::runLegacyHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) ); - wfProfileOut( __METHOD__ ); - return $this->mContent; } @@@ -372,6 -377,8 +370,6 @@@ return $this->mContentObject; } - wfProfileIn( __METHOD__ ); - $this->mContentLoaded = true; $this->mContent = null; @@@ -388,6 -395,7 +386,6 @@@ $this->mRevision = Revision::newFromId( $oldid ); if ( !$this->mRevision ) { wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" ); - wfProfileOut( __METHOD__ ); return false; } } @@@ -395,6 -403,7 +393,6 @@@ if ( !$this->mPage->getLatest() ) { wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" ); - wfProfileOut( __METHOD__ ); return false; } @@@ -403,6 -412,7 +401,6 @@@ if ( !$this->mRevision ) { wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" ); - wfProfileOut( __METHOD__ ); return false; } } @@@ -410,22 -420,16 +408,22 @@@ // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks. // We should instead work with the Revision object when we need it... // Loads if user is allowed - $this->mContentObject = $this->mRevision->getContent( + $content = $this->mRevision->getContent( Revision::FOR_THIS_USER, $this->getContext()->getUser() ); + + if ( !$content ) { + wfDebug( __METHOD__ . " failed to retrieve content of revision " . + $this->mRevision->getId() . "\n" ); + return false; + } + + $this->mContentObject = $content; $this->mRevIdFetched = $this->mRevision->getId(); Hooks::run( 'ArticleAfterFetchContentObject', array( &$this, &$this->mContentObject ) ); - wfProfileOut( __METHOD__ ); - return $this->mContentObject; } @@@ -476,6 -480,8 +474,6 @@@ public function view() { global $wgUseFileCache, $wgUseETag, $wgDebugToolbar, $wgMaxRedirects; - 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 @@@ -487,6 -493,7 +485,6 @@@ $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user ); if ( count( $permErrors ) ) { wfDebug( __METHOD__ . ": denied on secondary read check\n" ); - wfProfileOut( __METHOD__ ); throw new PermissionsError( 'read', $permErrors ); } @@@ -495,6 -502,7 +493,6 @@@ if ( $this->mRedirectUrl ) { $outputPage->redirect( $this->mRedirectUrl ); wfDebug( __METHOD__ . ": redirecting due to oldid\n" ); - wfProfileOut( __METHOD__ ); return; } @@@ -503,6 -511,7 +501,6 @@@ if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) { wfDebug( __METHOD__ . ": showing diff page\n" ); $this->showDiffPage(); - wfProfileOut( __METHOD__ ); return; } @@@ -557,6 -566,7 +555,6 @@@ # Is it client cached? if ( $outputPage->checkLastModified( $timestamp ) ) { wfDebug( __METHOD__ . ": done 304\n" ); - wfProfileOut( __METHOD__ ); return; # Try file cache @@@ -565,6 -575,7 +563,6 @@@ # tell wgOut that output is taken care of $outputPage->disable(); $this->mPage->doViewUpdates( $user, $oldid ); - wfProfileOut( __METHOD__ ); return; } @@@ -574,7 -585,7 +572,7 @@@ $useParserCache = $this->mPage->isParserCacheUsed( $parserOptions, $oldid ); wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" ); if ( $user->getStubThreshold() ) { - wfIncrStats( 'pcache_miss_stub' ); + $this->getContext()->getStats()->increment( 'pcache_miss_stub' ); } $this->showRedirectedFromHeader(); @@@ -597,6 -608,7 +595,6 @@@ wfDebug( __METHOD__ . ": showing missing article\n" ); $this->showMissingArticle(); $this->mPage->doViewUpdates( $user ); - wfProfileOut( __METHOD__ ); return; } @@@ -635,6 -647,7 +633,6 @@@ if ( !$this->showDeletedRevisionHeader() ) { wfDebug( __METHOD__ . ": cannot view deleted revision\n" ); - wfProfileOut( __METHOD__ ); return; } } @@@ -681,14 -694,16 +679,14 @@@ $outputPage->addWikiText( '
' . $errortext . '
' ); } # Connection or timeout error - wfProfileOut( __METHOD__ ); return; } $this->mParserOutput = $poolArticleView->getParserOutput(); $outputPage->addParserOutput( $this->mParserOutput ); if ( $content->getRedirectTarget() ) { - $outputPage->addSubtitle( - "" . wfMessage( 'redirectpagesub' )->parse() . "" - ); + $outputPage->addSubtitle( "" . + $this->getContext()->msg( 'redirectpagesub' )->parse() . "" ); } # Don't cache a dirty ParserOutput object @@@ -707,7 -722,7 +705,7 @@@ } # Get the ParserOutput actually *displayed* here. - # Note that $this->mParserOutput is the *current* version output. + # Note that $this->mParserOutput is the *current*/oldid version output. $pOutput = ( $outputDone instanceof ParserOutput ) ? $outputDone // object fetched by hook : $this->mParserOutput; @@@ -738,6 -753,7 +736,6 @@@ $outputPage->addModules( 'mediawiki.action.view.postEdit' ); - wfProfileOut( __METHOD__ ); } /** @@@ -756,8 -772,9 +754,8 @@@ * Show a diff page according to current request variables. For use within * Article::view() only, other callers should use the DifferenceEngine class. * - * @todo Make protected */ - public function showDiffPage() { + protected function showDiffPage() { $request = $this->getContext()->getRequest(); $user = $this->getContext()->getUser(); $diff = $request->getVal( 'diff' ); @@@ -816,7 -833,7 +814,7 @@@ if ( $showCacheHint ) { $dir = $this->getContext()->getLanguage()->getDir(); - $lang = $this->getContext()->getLanguage()->getCode(); + $lang = $this->getContext()->getLanguage()->getHtmlCode(); $outputPage->wrapWikiMsg( "
\n$1\n
", @@@ -958,10 -975,9 +956,10 @@@ */ public function showRedirectedFromHeader() { global $wgRedirectSources; - $outputPage = $this->getContext()->getOutput(); - $request = $this->getContext()->getRequest(); + $context = $this->getContext(); + $outputPage = $context->getOutput(); + $request = $context->getRequest(); $rdfrom = $request->getVal( 'rdfrom' ); // Construct a URL for the current page view, but with the target title @@@ -986,7 -1002,7 +984,7 @@@ ); $outputPage->addSubtitle( "" . - wfMessage( 'redirectedfrom' )->rawParams( $redir )->parse() + $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse() . "" ); // Add the script to update the displayed URL and @@@ -1010,7 -1026,7 +1008,7 @@@ if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) { $redir = Linker::makeExternalLink( $rdfrom, $rdfrom ); $outputPage->addSubtitle( "" . - wfMessage( 'redirectedfrom' )->rawParams( $redir )->parse() + $context->msg( 'redirectedfrom' )->rawParams( $redir )->parse() . "" ); // Add the script to update the displayed URL @@@ -1082,6 -1098,8 +1080,6 @@@ return false; } - wfProfileIn( __METHOD__ ); - // New page patrol: Get the timestamp of the oldest revison which // the revision table holds for the given page. Then we look // whether it's within the RC lifespan and if it is, we try @@@ -1090,6 -1108,7 +1088,6 @@@ // Check for cached results if ( $cache->get( wfMemcKey( 'NotPatrollablePage', $this->getTitle()->getArticleID() ) ) ) { - wfProfileOut( __METHOD__ ); return false; } @@@ -1098,6 -1117,7 +1096,6 @@@ ) { // The current revision is already older than what could be in the RC table // 6h tolerance because the RC might not be cleaned out regularly - wfProfileOut( __METHOD__ ); return false; } @@@ -1133,12 -1153,14 +1131,12 @@@ // Don't cache in case we can patrol as this could change $cache->set( wfMemcKey( 'NotPatrollablePage', $this->getTitle()->getArticleID() ), '1' ); - wfProfileOut( __METHOD__ ); return false; } - if ( $rc->getPerformer()->getName() == $user->getName() ) { + if ( $rc->getPerformer()->equals( $user ) ) { // Don't show a patrol link for own creations. If the user could // patrol them, they already would be patrolled - wfProfileOut( __METHOD__ ); return false; } @@@ -1168,6 -1190,7 +1166,6 @@@ '' ); - wfProfileOut( __METHOD__ ); return true; } @@@ -1325,8 -1348,7 +1323,8 @@@ return; } - $unhide = $this->getContext()->getRequest()->getInt( 'unhide' ) == 1; + $context = $this->getContext(); + $unhide = $context->getRequest()->getInt( 'unhide' ) == 1; # Cascade unhide param in links for easy deletion browsing $extraParams = array(); @@@ -1343,8 -1365,8 +1341,8 @@@ $timestamp = $revision->getTimestamp(); $current = ( $oldid == $this->mPage->getLatest() ); - $language = $this->getContext()->getLanguage(); - $user = $this->getContext()->getUser(); + $language = $context->getLanguage(); + $user = $context->getUser(); $td = $language->userTimeAndDate( $timestamp, $user ); $tddate = $language->userDate( $timestamp, $user ); @@@ -1353,13 -1375,13 +1351,13 @@@ # 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() + $infomsg = $current && !$context->msg( 'revision-info-current' )->isDisabled() ? 'revision-info-current' : 'revision-info'; - $outputPage = $this->getContext()->getOutput(); + $outputPage = $context->getOutput(); $outputPage->addSubtitle( "
" . - wfMessage( $infomsg, $td ) + $context->msg( $infomsg, $td ) ->rawParams( $userlinks ) ->params( $revision->getID(), $tddate, $tdtime, $revision->getUserText() ) ->rawParams( Linker::revComment( $revision, true, true ) ) @@@ -1368,18 -1390,18 +1366,18 @@@ ); $lnk = $current - ? wfMessage( 'currentrevisionlink' )->escaped() + ? $context->msg( 'currentrevisionlink' )->escaped() : Linker::linkKnown( $this->getTitle(), - wfMessage( 'currentrevisionlink' )->escaped(), + $context->msg( 'currentrevisionlink' )->escaped(), array(), $extraParams ); $curdiff = $current - ? wfMessage( 'diff' )->escaped() + ? $context->msg( 'diff' )->escaped() : Linker::linkKnown( $this->getTitle(), - wfMessage( 'diff' )->escaped(), + $context->msg( 'diff' )->escaped(), array(), array( 'diff' => 'cur', @@@ -1390,30 -1412,30 +1388,30 @@@ $prevlink = $prev ? Linker::linkKnown( $this->getTitle(), - wfMessage( 'previousrevision' )->escaped(), + $context->msg( 'previousrevision' )->escaped(), array(), array( 'direction' => 'prev', 'oldid' => $oldid ) + $extraParams ) - : wfMessage( 'previousrevision' )->escaped(); + : $context->msg( 'previousrevision' )->escaped(); $prevdiff = $prev ? Linker::linkKnown( $this->getTitle(), - wfMessage( 'diff' )->escaped(), + $context->msg( 'diff' )->escaped(), array(), array( 'diff' => 'prev', 'oldid' => $oldid ) + $extraParams ) - : wfMessage( 'diff' )->escaped(); + : $context->msg( 'diff' )->escaped(); $nextlink = $current - ? wfMessage( 'nextrevision' )->escaped() + ? $context->msg( 'nextrevision' )->escaped() : Linker::linkKnown( $this->getTitle(), - wfMessage( 'nextrevision' )->escaped(), + $context->msg( 'nextrevision' )->escaped(), array(), array( 'direction' => 'next', @@@ -1421,10 -1443,10 +1419,10 @@@ ) + $extraParams ); $nextdiff = $current - ? wfMessage( 'diff' )->escaped() + ? $context->msg( 'diff' )->escaped() : Linker::linkKnown( $this->getTitle(), - wfMessage( 'diff' )->escaped(), + $context->msg( 'diff' )->escaped(), array(), array( 'diff' => 'next', @@@ -1438,7 -1460,7 +1436,7 @@@ } $outputPage->addSubtitle( "
" . $cdel . - wfMessage( 'revision-nav' )->rawParams( + $context->msg( 'revision-nav' )->rawParams( $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff )->escaped() . "
" ); } @@@ -1458,7 -1480,7 +1456,7 @@@ $lang = $this->getTitle()->getPageLanguage(); $out = $this->getContext()->getOutput(); if ( $appendSubtitle ) { - $out->addSubtitle( wfMessage( 'redirectpagesub' )->parse() ); + $out->addSubtitle( wfMessage( 'redirectpagesub' ) ); } $out->addModuleStyles( 'mediawiki.action.view.redirectPage' ); return static::getRedirectHeaderHtml( $lang, $target, $forceKnown ); @@@ -1494,9 -1516,8 +1492,9 @@@ ( $forceKnown ? array( 'known', 'noclasses' ) : array() ) ) . ''; } + $html .= ''; - $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->text(); + $redirectToText = wfMessage( 'redirectto' )->inLanguage( $lang )->escaped(); return '
' . '

' . $redirectToText . '

' . @@@ -1603,7 -1624,7 +1601,7 @@@ if ( !$reason ) { try { $reason = $this->generateReason( $hasHistory ); - } catch ( MWException $e ) { + } catch ( Exception $e ) { # if a page is horribly broken, we still want to be able to # delete it. So be lenient about errors here. wfDebug( "Error while building auto delete summary: $e" ); @@@ -1634,7 -1655,7 +1632,7 @@@ $context->msg( 'historywarning' )->numParams( $revisions )->parse() . $context->msg( 'word-separator' )->escaped() . Linker::linkKnown( $title, $context->msg( 'history' )->escaped(), - array( 'rel' => 'archives' ), + array(), array( 'action' => 'history' ) ) . '' ); diff --combined includes/page/WikiPage.php index 285611d93b,cb2e396983..5527ace2f7 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@@ -31,8 -31,6 +31,6 @@@ interface Page * * Some fields are public only for backwards-compatibility. Use accessors. * In the past, this class was part of Article.php and everything was public. - * - * @internal documentation reviewed 15 Mar 2010 */ class WikiPage implements Page, IDBAccessObject { // Constants for $mDataLoadedFrom and related @@@ -557,6 -555,7 +555,6 @@@ * @return Revision|null */ public function getOldestRevision() { - wfProfileIn( __METHOD__ ); // Try using the slave database first, then try the master $continue = 2; @@@ -587,6 -586,7 +585,6 @@@ } } - wfProfileOut( __METHOD__ ); return $row ? Revision::newFromRow( $row ) : null; } @@@ -1053,6 -1053,7 +1051,6 @@@ * @return array Array of authors, duplicates not removed */ public function getLastNAuthors( $num, $revLatest = 0 ) { - wfProfileIn( __METHOD__ ); // First try the slave // If that doesn't have the latest revision, try the master $continue = 2; @@@ -1073,6 -1074,7 +1071,6 @@@ ); if ( !$res ) { - wfProfileOut( __METHOD__ ); return array(); } @@@ -1092,6 -1094,7 +1090,6 @@@ $authors[] = $row->rev_user_text; } - wfProfileOut( __METHOD__ ); return $authors; } @@@ -1124,6 -1127,7 +1122,6 @@@ * @return ParserOutput|bool ParserOutput or false if the revision was not found */ public function getParserOutput( ParserOptions $parserOptions, $oldid = null ) { - wfProfileIn( __METHOD__ ); $useParserCache = $this->isParserCacheUsed( $parserOptions, $oldid ); wfDebug( __METHOD__ . ': using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" ); @@@ -1134,6 -1138,7 +1132,6 @@@ if ( $useParserCache ) { $parserOutput = ParserCache::singleton()->get( $this, $parserOptions ); if ( $parserOutput !== false ) { - wfProfileOut( __METHOD__ ); return $parserOutput; } } @@@ -1145,6 -1150,8 +1143,6 @@@ $pool = new PoolWorkArticleView( $this, $parserOptions, $oldid, $useParserCache ); $pool->execute(); - wfProfileOut( __METHOD__ ); - return $pool->getParserOutput(); } @@@ -1219,6 -1226,7 +1217,6 @@@ * @return int The newly created page_id key, or false if the title already existed */ public function insertOn( $dbw ) { - wfProfileIn( __METHOD__ ); $page_id = $dbw->nextSequenceValue( 'page_page_id_seq' ); $dbw->insert( 'page', array( @@@ -1241,6 -1249,7 +1239,6 @@@ $this->mId = $newid; $this->mTitle->resetArticleID( $newid ); } - wfProfileOut( __METHOD__ ); return $affected ? $newid : false; } @@@ -1263,6 -1272,8 +1261,6 @@@ ) { global $wgContentHandlerUseDB; - wfProfileIn( __METHOD__ ); - $content = $revision->getContent(); $len = $content ? $content->getSize() : 0; $rt = $content ? $content->getUltimateRedirectTarget() : null; @@@ -1304,6 -1315,7 +1302,6 @@@ $this->mLatest, $revision->getContentModel() ); } - wfProfileOut( __METHOD__ ); return $result; } @@@ -1328,6 -1340,7 +1326,6 @@@ return true; } - wfProfileIn( __METHOD__ ); if ( $isRedirect ) { $this->insertRedirectEntry( $redirectTitle ); } else { @@@ -1339,6 -1352,7 +1337,6 @@@ if ( $this->getTitle()->getNamespace() == NS_FILE ) { RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $this->getTitle() ); } - wfProfileOut( __METHOD__ ); return ( $dbw->affectedRows() != 0 ); } @@@ -1354,6 -1368,7 +1352,6 @@@ * @return bool */ public function updateIfNewerOn( $dbw, $revision ) { - wfProfileIn( __METHOD__ ); $row = $dbw->selectRow( array( 'revision', 'page' ), @@@ -1365,6 -1380,7 +1363,6 @@@ if ( $row ) { if ( wfTimestamp( TS_MW, $row->rev_timestamp ) >= $revision->getTimestamp() ) { - wfProfileOut( __METHOD__ ); return false; } $prev = $row->rev_id; @@@ -1377,6 -1393,7 +1375,6 @@@ $ret = $this->updateRevisionOn( $dbw, $revision, $prev, $lastRevIsRedirect ); - wfProfileOut( __METHOD__ ); return $ret; } @@@ -1495,6 -1512,7 +1493,6 @@@ */ public function replaceSectionContent( $sectionId, Content $sectionContent, $sectionTitle = '', $edittime = null ) { - wfProfileIn( __METHOD__ ); $baseRevId = null; if ( $edittime && $sectionId !== 'new' ) { @@@ -1505,6 -1523,7 +1503,6 @@@ } } - wfProfileOut( __METHOD__ ); return $this->replaceSectionAtRev( $sectionId, $sectionContent, $sectionTitle, $baseRevId ); } @@@ -1524,12 -1543,14 +1522,12 @@@ public function replaceSectionAtRev( $sectionId, Content $sectionContent, $sectionTitle = '', $baseRevId = null ) { - wfProfileIn( __METHOD__ ); if ( strval( $sectionId ) === '' ) { // Whole-page edit; let the whole text through $newContent = $sectionContent; } else { if ( !$this->supportsSections() ) { - wfProfileOut( __METHOD__ ); throw new MWException( "sections not supported for content model " . $this->getContentHandler()->getModelID() ); } @@@ -1545,6 -1566,7 +1543,6 @@@ if ( !$rev ) { wfDebug( __METHOD__ . " asked for bogus section (page: " . $this->getId() . "; section: $sectionId)\n" ); - wfProfileOut( __METHOD__ ); return null; } @@@ -1553,12 -1575,14 +1551,12 @@@ if ( !$oldContent ) { wfDebug( __METHOD__ . ": no page text\n" ); - wfProfileOut( __METHOD__ ); return null; } $newContent = $oldContent->replaceSection( $sectionId, $sectionContent, $sectionTitle ); } - wfProfileOut( __METHOD__ ); return $newContent; } @@@ -1608,9 -1632,7 +1606,9 @@@ * error will be returned. These two conditions are also possible with * auto-detection due to MediaWiki's performance-optimised locking strategy. * - * @param bool|int $baseRevId The revision ID this edit was based off, if any + * @param bool|int $baseRevId The revision ID this edit was based off, if any. + * This is not the parent revision ID, rather the revision ID for older + * content used as the source for a rollback, for example. * @param User $user The user doing the edit * * @throws MWException @@@ -1670,9 -1692,7 +1668,9 @@@ * error will be returned. These two conditions are also possible with * auto-detection due to MediaWiki's performance-optimised locking strategy. * - * @param bool|int $baseRevId The revision ID this edit was based off, if any + * @param bool|int $baseRevId The revision ID this edit was based off, if any. + * This is not the parent revision ID, rather the revision ID for older + * content used as the source for a rollback, for example. * @param User $user The user doing the edit * @param string $serialFormat Format for storing the content in the * database. @@@ -1704,7 -1724,10 +1702,7 @@@ throw new MWException( 'Something is trying to edit an article with an empty title' ); } - wfProfileIn( __METHOD__ ); - if ( !$content->getContentHandler()->canBeUsedOn( $this->getTitle() ) ) { - wfProfileOut( __METHOD__ ); return Status::newFatal( 'content-not-allowed-here', ContentHandler::getLocalizedName( $content->getModel() ), $this->getTitle()->getPrefixedText() ); @@@ -1733,6 -1756,7 +1731,6 @@@ $status->fatal( 'edit-hook-aborted' ); } - wfProfileOut( __METHOD__ ); return $status; } @@@ -1779,9 -1803,11 +1777,9 @@@ wfDebug( __METHOD__ . ": EDIT_UPDATE specified but article doesn't exist\n" ); $status->fatal( 'edit-gone-missing' ); - wfProfileOut( __METHOD__ ); return $status; } elseif ( !$old_content ) { // Sanity check for bug 37225 - wfProfileOut( __METHOD__ ); throw new MWException( "Could not find text for current revision {$oldid}." ); } @@@ -1806,12 -1832,13 +1804,12 @@@ $dbw->begin( __METHOD__ ); try { - $prepStatus = $content->prepareSave( $this, $flags, $baseRevId, $user ); + $prepStatus = $content->prepareSave( $this, $flags, $oldid, $user ); $status->merge( $prepStatus ); if ( !$status->isOK() ) { $dbw->rollback( __METHOD__ ); - wfProfileOut( __METHOD__ ); return $status; } $revisionId = $revision->insertOn( $dbw ); @@@ -1827,6 -1854,7 +1825,6 @@@ $dbw->rollback( __METHOD__ ); - wfProfileOut( __METHOD__ ); return $status; } @@@ -1848,7 -1876,7 +1846,7 @@@ } } $user->incEditCount(); - } catch ( MWException $e ) { + } catch ( Exception $e ) { $dbw->rollback( __METHOD__ ); // Question: Would it perhaps be better if this method turned all // exceptions into $status's? @@@ -1885,12 -1913,13 +1883,12 @@@ $dbw->begin( __METHOD__ ); try { - $prepStatus = $content->prepareSave( $this, $flags, $baseRevId, $user ); + $prepStatus = $content->prepareSave( $this, $flags, $oldid, $user ); $status->merge( $prepStatus ); if ( !$status->isOK() ) { $dbw->rollback( __METHOD__ ); - wfProfileOut( __METHOD__ ); return $status; } @@@ -1904,6 -1933,7 +1902,6 @@@ $dbw->rollback( __METHOD__ ); $status->fatal( 'edit-already-exists' ); - wfProfileOut( __METHOD__ ); return $status; } @@@ -1951,7 -1981,7 +1949,7 @@@ } $user->incEditCount(); - } catch ( MWException $e ) { + } catch ( Exception $e ) { $dbw->rollback( __METHOD__ ); throw $e; } @@@ -1986,6 -2016,7 +1984,6 @@@ $user->addAutopromoteOnceGroups( 'onEdit' ); } ); - wfProfileOut( __METHOD__ ); return $status; } @@@ -2033,8 -2064,7 +2031,8 @@@ * Returns a stdClass with source, pst and output members * * @param Content $content - * @param int|null $revid + * @param Revision|int|null $revision Revision object. For backwards compatibility, a + * revision ID is also accepted, but this is deprecated. * @param User|null $user * @param string|null $serialFormat * @param bool $useCache Check shared prepared edit cache @@@ -2044,22 -2074,9 +2042,22 @@@ * @since 1.21 */ public function prepareContentForEdit( - Content $content, $revid = null, User $user = null, $serialFormat = null, $useCache = true + Content $content, $revision = null, User $user = null, $serialFormat = null, $useCache = true ) { - global $wgContLang, $wgUser; + global $wgContLang, $wgUser, $wgAjaxEditStash; + + if ( is_object( $revision ) ) { + $revid = $revision->getId(); + } else { + $revid = $revision; + // This code path is deprecated, and nothing is known to + // use it, so performance here shouldn't be a worry. + if ( $revid !== null ) { + $revision = Revision::newFromId( $revid, Revision::READ_LATEST ); + } else { + $revision = null; + } + } $user = is_null( $user ) ? $wgUser : $user; //XXX: check $user->getId() here??? @@@ -2081,7 -2098,7 +2079,7 @@@ } // The edit may have already been prepared via api.php?action=stashedit - $cachedEdit = $useCache + $cachedEdit = $useCache && $wgAjaxEditStash ? ApiStashEdit::checkCache( $this->getTitle(), $content, $user ) : false; @@@ -2110,25 -2127,6 +2108,25 @@@ if ( $cachedEdit ) { $edit->output = $cachedEdit->output; } else { + if ( $revision ) { + // We get here if vary-revision is set. This means that this page references + // itself (such as via self-transclusion). In this case, we need to make sure + // that any such self-references refer to the newly-saved revision, and not + // to the previous one, which could otherwise happen due to slave lag. + $oldCallback = $edit->popts->setCurrentRevisionCallback( + function ( $title, $parser = false ) use ( $revision, &$oldCallback ) { + if ( $title->equals( $revision->getTitle() ) ) { + return $revision; + } else { + return call_user_func( + $oldCallback, + $title, + $parser + ); + } + } + ); + } $edit->output = $edit->pstContent ? $edit->pstContent->getParserOutput( $this->mTitle, $revid, $edit->popts ) : null; @@@ -2158,16 -2156,16 +2156,16 @@@ * - changed: boolean, whether the revision changed the content (default true) * - created: boolean, whether the revision created the page (default false) * - moved: boolean, whether the page was moved (default false) - * - oldcountable: boolean or null (default null): + * - oldcountable: boolean, null, or string 'no-change' (default null): * - boolean: whether the page was counted as an article before that * revision, only used in changed is true and created is false - * - null: don't change the article count + * - null: if created is false, don't update the article count; if created + * is true, do update the article count + * - 'no-change': don't update the article count, ever */ public function doEditUpdates( Revision $revision, User $user, array $options = array() ) { global $wgEnableParserCache; - wfProfileIn( __METHOD__ ); - $options += array( 'changed' => true, 'created' => false, @@@ -2181,7 -2179,7 +2179,7 @@@ // already pre-save transformed once. if ( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) { wfDebug( __METHOD__ . ": No prepared edit or vary-revision is set...\n" ); - $editInfo = $this->prepareContentForEdit( $content, $revision->getId(), $user ); + $editInfo = $this->prepareContentForEdit( $content, $revision, $user ); } else { wfDebug( __METHOD__ . ": No vary-revision, using prepared edit...\n" ); $editInfo = $this->mPreparedEdit; @@@ -2206,11 -2204,15 +2204,11 @@@ Hooks::run( 'ArticleEditUpdates', array( &$this, &$editInfo, $options['changed'] ) ); if ( Hooks::run( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) { - if ( 0 == mt_rand( 0, 99 ) ) { - // Flush old entries from the `recentchanges` table; we do this on - // random requests so as to avoid an increase in writes for no good reason - RecentChange::purgeExpiredChanges(); - } + // Flush old entries from the `recentchanges` table + JobQueueGroup::singleton()->push( RecentChangesUpdateJob::newPurgeJob() ); } if ( !$this->exists() ) { - wfProfileOut( __METHOD__ ); return; } @@@ -2218,9 -2220,7 +2216,9 @@@ $title = $this->mTitle->getPrefixedDBkey(); $shortTitle = $this->mTitle->getDBkey(); - if ( !$options['changed'] && !$options['moved'] ) { + if ( $options['oldcountable'] === 'no-change' || + ( !$options['changed'] && !$options['moved'] ) + ) { $good = 0; } elseif ( $options['created'] ) { $good = (int)$this->isCountable( $editInfo ); @@@ -2277,6 -2277,7 +2275,6 @@@ self::onArticleEdit( $this->mTitle ); } - wfProfileOut( __METHOD__ ); } /** @@@ -2312,6 -2313,7 +2310,6 @@@ public function doQuickEditContent( Content $content, User $user, $comment = '', $minor = false, $serialFormat = null ) { - wfProfileIn( __METHOD__ ); $serialized = $content->serialize( $serialFormat ); @@@ -2331,6 -2333,7 +2329,6 @@@ Hooks::run( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) ); - wfProfileOut( __METHOD__ ); } /** @@@ -3021,8 -3024,8 +3019,8 @@@ // Get the last edit not by this guy... // Note: these may not be public values - $user = intval( $current->getRawUser() ); - $user_text = $dbw->addQuotes( $current->getRawUserText() ); + $user = intval( $current->getUser( Revision::RAW ) ); + $user_text = $dbw->addQuotes( $current->getUserText( Revision::RAW ) ); $s = $dbw->selectRow( 'revision', array( 'rev_id', 'rev_timestamp', 'rev_deleted' ), array( 'rev_page' => $current->getPage(), @@@ -3065,7 -3068,7 +3063,7 @@@ } // Generate the edit summary if necessary - $target = Revision::newFromId( $s->rev_id ); + $target = Revision::newFromId( $s->rev_id, Revision::READ_LATEST ); if ( empty( $summary ) ) { if ( $from == '' ) { // no public user name $summary = wfMessage( 'revertpage-nouser' ); @@@ -3151,9 -3154,13 +3149,9 @@@ * * @param Title $title */ - public static function onArticleCreate( $title ) { + public static function onArticleCreate( Title $title ) { // Update existence markers on article/talk tabs... - if ( $title->isTalkPage() ) { - $other = $title->getSubjectPage(); - } else { - $other = $title->getTalkPage(); - } + $other = $title->getOtherPage(); $other->invalidateCache(); $other->purgeSquid(); @@@ -3168,9 -3175,13 +3166,9 @@@ * * @param Title $title */ - public static function onArticleDelete( $title ) { + public static function onArticleDelete( Title $title ) { // Update existence markers on article/talk tabs... - if ( $title->isTalkPage() ) { - $other = $title->getSubjectPage(); - } else { - $other = $title->getTalkPage(); - } + $other = $title->getOtherPage(); $other->invalidateCache(); $other->purgeSquid(); @@@ -3209,8 -3220,10 +3207,8 @@@ * Purge caches on page update etc * * @param Title $title - * @todo Verify that $title is always a Title object (and never false or - * null), add Title hint to parameter $title. */ - public static function onArticleEdit( $title ) { + public static function onArticleEdit( Title $title ) { // Invalidate caches of articles which include this page DeferredUpdates::addHTMLCacheUpdate( $title, 'templatelinks' ); @@@ -3382,37 -3395,70 +3380,37 @@@ } /** - * Updates cascading protections + * Opportunistically enqueue link update jobs given fresh parser output if useful * - * @param ParserOutput $parserOutput ParserOutput object for the current version + * @param ParserOutput $parserOutput Current version page output + * @return bool Whether a job was pushed + * @since 1.25 */ - public function doCascadeProtectionUpdates( ParserOutput $parserOutput ) { - if ( wfReadOnly() || !$this->mTitle->areRestrictionsCascading() ) { - return; - } - - // templatelinks or imagelinks tables 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 and images from imagelinks - $id = $this->getId(); - - $dbLinks = array(); - - $dbr = wfGetDB( DB_SLAVE ); - $res = $dbr->select( array( 'templatelinks' ), - array( 'tl_namespace', 'tl_title' ), - array( 'tl_from' => $id ), - __METHOD__ - ); - - foreach ( $res as $row ) { - $dbLinks["{$row->tl_namespace}:{$row->tl_title}"] = true; + public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) { + if ( wfReadOnly() ) { + return false; } - $dbr = wfGetDB( DB_SLAVE ); - $res = $dbr->select( array( 'imagelinks' ), - array( 'il_to' ), - array( 'il_from' => $id ), - __METHOD__ - ); - - foreach ( $res as $row ) { - $dbLinks[NS_FILE . ":{$row->il_to}"] = true; + if ( $this->mTitle->areRestrictionsCascading() ) { + // If the page is cascade protecting, the links should really be up-to-date + $params = array( 'prioritize' => true ); + } elseif ( $parserOutput->hasDynamicContent() ) { + // Assume the output contains time/random based magic words + $params = array(); + } else { + // If the inclusions are deterministic, the edit-triggered link jobs are enough + return false; } - // Get templates and images from parser output. - $poLinks = array(); - foreach ( $parserOutput->getTemplates() as $ns => $templates ) { - foreach ( $templates as $dbk => $id ) { - $poLinks["$ns:$dbk"] = true; - } - } - foreach ( $parserOutput->getImages() as $dbk => $id ) { - $poLinks[NS_FILE . ":$dbk"] = true; + // Check if the last link refresh was before page_touched + if ( $this->getLinksTimestamp() < $this->getTouched() ) { + JobQueueGroup::singleton()->push( EnqueueJob::newFromLocalJobs( + new JobSpecification( 'refreshLinks', $params, array(), $this->mTitle ) + ) ); + return true; } - // Get the diff - $links_diff = array_diff_key( $poLinks, $dbLinks ); - - if ( count( $links_diff ) > 0 ) { - // Whee, link updates time. - // Note: we are only interested in links here. We don't need to get - // other DataUpdate items from the parser output. - $u = new LinksUpdate( $this->mTitle, $parserOutput, false ); - $u->doUpdate(); - } + return false; } /**