X-Git-Url: https://git.cyclocoop.org/%242?a=blobdiff_plain;f=includes%2FOutputPage.php;h=3f10c06ba9145f228893869ce189dde8ccf36176;hb=5f55e9c9c2a2416604c6b71a345353b73a960c50;hp=91fc75c9bd68de8ae0139aa0ea54e45cf04877bd;hpb=fb52b789201fcad7a243c13f4371a69d1976369a;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/OutputPage.php b/includes/OutputPage.php index 91fc75c9bd..3f10c06ba9 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -302,6 +302,11 @@ class OutputPage extends ContextSource { /** @var array Profiling data */ private $limitReportJSData = []; + /** + * Link: header contents + */ + private $mLinkHeader = []; + /** * Constructor for OutputPage. This should not be called directly. * Instead a new RequestContext should be created and it will implicitly create @@ -534,14 +539,32 @@ class OutputPage extends ContextSource { if ( $module instanceof ResourceLoaderModule && $module->getOrigin() <= $this->getAllowedModules( $type ) && ( is_null( $position ) || $module->getPosition() == $position ) - && ( !$this->mTarget || in_array( $this->mTarget, $module->getTargets() ) ) ) { + if ( $this->mTarget && !in_array( $this->mTarget, $module->getTargets() ) ) { + $this->warnModuleTargetFilter( $module->getName() ); + continue; + } $filteredModules[] = $val; } } return $filteredModules; } + private function warnModuleTargetFilter( $moduleName ) { + static $warnings = []; + if ( isset( $warnings[$this->mTarget][$moduleName] ) ) { + return; + } + $warnings[$this->mTarget][$moduleName] = true; + $this->getResourceLoader()->getLogger()->debug( + 'Module "{module}" not loadable on target "{target}".', + [ + 'module' => $moduleName, + 'target' => $this->mTarget, + ] + ); + } + /** * Get the list of modules to include on this page * @@ -763,7 +786,7 @@ class OutputPage extends ContextSource { 'epoch' => $config->get( 'CacheEpoch' ) ]; if ( $config->get( 'UseSquid' ) ) { - // bug 44570: the core page itself may not change, but resources might + // T46570: the core page itself may not change, but resources might $modifiedTimes['sepoch'] = wfTimestamp( TS_MW, time() - $config->get( 'SquidMaxage' ) ); } Hooks::run( 'OutputPageCheckLastModified', [ &$modifiedTimes, $this ] ); @@ -1448,7 +1471,7 @@ class OutputPage extends ContextSource { ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL ); - // Site-wide styles are controlled by a config setting, see bug 71621 + // Site-wide styles are controlled by a config setting, see T73621 // for background on why. User styles are never allowed. if ( $this->getConfig()->get( 'AllowSiteCSSOnRestrictedPages' ) ) { $styleOrigin = ResourceLoaderModule::ORIGIN_USER_SITEWIDE; @@ -1550,6 +1573,7 @@ class OutputPage extends ContextSource { // been changed somehow, and keep it if so. $anonPO = ParserOptions::newFromAnon(); $anonPO->setEditSection( false ); + $anonPO->setAllowUnsafeRawHtml( false ); if ( !$options->matches( $anonPO ) ) { wfLogWarning( __METHOD__ . ': Setting a changed bogus ParserOptions: ' . wfGetAllCallers( 5 ) ); $options->isBogus = false; @@ -1563,6 +1587,7 @@ class OutputPage extends ContextSource { // either. $po = ParserOptions::newFromAnon(); $po->setEditSection( false ); + $po->setAllowUnsafeRawHtml( false ); $po->isBogus = true; if ( $options !== null ) { $this->mParserOptions = empty( $options->isBogus ) ? $options : null; @@ -1572,6 +1597,7 @@ class OutputPage extends ContextSource { $this->mParserOptions = ParserOptions::newFromContext( $this->getContext() ); $this->mParserOptions->setEditSection( false ); + $this->mParserOptions->setAllowUnsafeRawHtml( false ); } if ( $options !== null && !empty( $options->isBogus ) ) { @@ -2084,6 +2110,28 @@ class OutputPage extends ContextSource { return 'Vary: ' . implode( ', ', array_keys( $this->mVaryHeader ) ); } + /** + * Add an HTTP Link: header + * + * @param string $header Header value + */ + public function addLinkHeader( $header ) { + $this->mLinkHeader[] = $header; + } + + /** + * Return a Link: header. Based on the values of $mLinkHeader. + * + * @return string + */ + public function getLinkHeader() { + if ( !$this->mLinkHeader ) { + return false; + } + + return 'Link: ' . implode( ',', $this->mLinkHeader ); + } + /** * Get a complete Key header * @@ -2340,6 +2388,12 @@ class OutputPage extends ContextSource { // jQuery etc. can work correctly. $response->header( 'X-UA-Compatible: IE=Edge' ); + $this->addLogoPreloadLinkHeaders(); + $linkHeader = $this->getLinkHeader(); + if ( $linkHeader ) { + $response->header( $linkHeader ); + } + // Prevent framing, if requested $frameOptions = $this->getFrameOptions(); if ( $frameOptions ) { @@ -2484,7 +2538,7 @@ class OutputPage extends ContextSource { ) { $displayReturnto = null; - # Due to bug 32276, if a user does not have read permissions, + # Due to T34276, if a user does not have read permissions, # $this->getTitle() will just give Special:Badtitle, which is # not especially useful as a returnto parameter. Use the title # from the request instead, if there was one. @@ -2706,7 +2760,9 @@ class OutputPage extends ContextSource { } else { $titleObj = Title::newFromText( $returnto ); } - if ( !is_object( $titleObj ) ) { + // We don't want people to return to external interwiki. That + // might potentially be used as part of a phishing scheme + if ( !is_object( $titleObj ) || $titleObj->isExternal() ) { $titleObj = Title::newMainPage(); } @@ -3066,7 +3122,7 @@ class OutputPage extends ContextSource { $curRevisionId = 0; $articleId = 0; - $canonicalSpecialPageName = false; # bug 21115 + $canonicalSpecialPageName = false; # T23115 $title = $this->getTitle(); $ns = $title->getNamespace(); @@ -3076,7 +3132,7 @@ class OutputPage extends ContextSource { $sk = $this->getSkin(); // Get the relevant title so that AJAX features can use the correct page name - // when making API requests from certain special pages (bug 34972). + // when making API requests from certain special pages (T36972). $relevantTitle = $sk->getRelevantTitle(); $relevantUser = $sk->getRelevantUser(); @@ -3697,6 +3753,8 @@ class OutputPage extends ContextSource { */ public static function transformResourcePath( Config $config, $path ) { global $IP; + + $localDir = $IP; $remotePathPrefix = $config->get( 'ResourceBasePath' ); if ( $remotePathPrefix === '' ) { // The configured base path is required to be empty string for @@ -3710,8 +3768,18 @@ class OutputPage extends ContextSource { // - Path is protocol-relative. Fixes T155310. Not supported by RelPath lib. return $path; } + // For files in resources, extensions/ or skins/, ResourceBasePath is preferred here. + // For other misc files in $IP, we'll fallback to that as well. There is, however, a fourth + // supported dir/path pair in the configuration (wgUploadDirectory, wgUploadPath) + // which is not expected to be in wgResourceBasePath on CDNs. (T155146) + $uploadPath = $config->get( 'UploadPath' ); + if ( strpos( $path, $uploadPath ) === 0 ) { + $localDir = $config->get( 'UploadDirectory' ); + $remotePathPrefix = $remotePath = $uploadPath; + } + $path = RelPath\getRelativePath( $path, $remotePath ); - return self::transformFilePath( $remotePathPrefix, $IP, $path ); + return self::transformFilePath( $remotePathPrefix, $localDir, $path ); } /** @@ -3825,7 +3893,7 @@ class OutputPage extends ContextSource { * $wgOut->addWikiText( "
\n" * . wfMessage( 'some-error' )->plain() . "\n
" ); * - * The newline after the opening div is needed in some wikitext. See bug 19226. + * The newline after the opening div is needed in some wikitext. See T21226. * * @param string $wrap */ @@ -3925,4 +3993,82 @@ class OutputPage extends ContextSource { 'mediawiki.widgets.styles', ] ); } + + /** + * Add Link headers for preloading the wiki's logo. + * + * @since 1.26 + */ + protected function addLogoPreloadLinkHeaders() { + $logo = $this->getConfig()->get( 'Logo' ); // wgLogo + $logoHD = $this->getConfig()->get( 'LogoHD' ); // wgLogoHD + + $tags = []; + $logosPerDppx = []; + $logos = []; + + $logosPerDppx['1.0'] = $logo; + + if ( !$logoHD ) { + // No media queries required if we only have one variant + $this->addLinkHeader( '<' . $logo . '>;rel=preload;as=image' ); + return; + } + + foreach ( $logoHD as $dppx => $src ) { + // Only 1.5x and 2x are supported + // Note: Keep in sync with ResourceLoaderSkinModule + if ( in_array( $dppx, [ '1.5x', '2x' ] ) ) { + // LogoHD uses a string in this format: "1.5x" + $dppx = substr( $dppx, 0, -1 ); + $logosPerDppx[$dppx] = $src; + } + } + + // Because PHP can't have floats as array keys + uksort( $logosPerDppx, function ( $a , $b ) { + $a = floatval( $a ); + $b = floatval( $b ); + + if ( $a == $b ) { + return 0; + } + // Sort from smallest to largest (e.g. 1x, 1.5x, 2x) + return ( $a < $b ) ? -1 : 1; + } ); + + foreach ( $logosPerDppx as $dppx => $src ) { + $logos[] = [ 'dppx' => $dppx, 'src' => $src ]; + } + + $logosCount = count( $logos ); + // Logic must match ResourceLoaderSkinModule: + // - 1x applies to resolution < 1.5dppx + // - 1.5x applies to resolution >= 1.5dppx && < 2dppx + // - 2x applies to resolution >= 2dppx + // Note that min-resolution and max-resolution are both inclusive. + for ( $i = 0; $i < $logosCount; $i++ ) { + if ( $i === 0 ) { + // Smallest dppx + // min-resolution is ">=" (larger than or equal to) + // "not min-resolution" is essentially "<" + $media_query = 'not all and (min-resolution: ' . $logos[ 1 ]['dppx'] . 'dppx)'; + } elseif ( $i !== $logosCount - 1 ) { + // In between + // Media query expressions can only apply "not" to the entire expression + // (e.g. can't express ">= 1.5 and not >= 2). + // Workaround: Use <= 1.9999 in place of < 2. + $upper_bound = floatval( $logos[ $i + 1 ]['dppx'] ) - 0.000001; + $media_query = '(min-resolution: ' . $logos[ $i ]['dppx'] . + 'dppx) and (max-resolution: ' . $upper_bound . 'dppx)'; + } else { + // Largest dppx + $media_query = '(min-resolution: ' . $logos[ $i ]['dppx'] . 'dppx)'; + } + + $this->addLinkHeader( + '<' . $logos[$i]['src'] . '>;rel=preload;as=image;media=' . $media_query + ); + } + } }