Yurik has uploaded a new change for review. https://gerrit.wikimedia.org/r/184995
Change subject: TLS support ...................................................................... TLS support Original patches with comments: Combined Ie9a845b5f2861f1c96f21c3def55043af9fe9c51 and I5f8414baaab805715a990ad788111e50171a7fe1 Show noscript HTTPS to HTTP tap if HTTPS charged This supports a tap-through option for users on <noscript> devices on HTTPS when HTTPS isn't zero-rated. The HTTPS downgrade verbiage is visible if the configuration for the operator is updated with a "showHttpsDowngrade" option for the particular connection type (e.g., "DIRECT") used by the user. The tap-through action is a link to http://zero.wikipedia.org/ , which has relatively graceful redirection. Additionally, for the zerodot-not-supported scenario where a red banner image is shown, a tap-through is exposed for http://zero.wikipedia.org/, which will have similar benefits. Finally, for the zerodot-IS-supported scenario where the operator's banner is shown as a GIF for these RLI users on HTTPS (rare), the tap-through link is in place, mainly to avoid complicating the code base for this relatively small portion of users (approximately 6.5% of users on Wikipedia Zero). It can be thought of as a feature to get the user back to the landing page / main page pertinent for them. Per the business development team, HTTPS downgrades should have a gray background. Thus the foreground text is made black in this case. Change-Id: I91aa173f915310cba5de75866b89059d9b8e3d91 --- M ZeroBanner.php M i18n/en.json M i18n/qqq.json M includes/PageRendering.php M includes/ZeroConfig.php M includes/ZeroSpecialPage.php M modules/ZeroOverlay.js M modules/banner.css M modules/interstitial.js A templates/interstitialask.hogan 10 files changed, 163 insertions(+), 19 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/ZeroBanner refs/changes/95/184995/1 diff --git a/ZeroBanner.php b/ZeroBanner.php index a7d664f..dff6c5c 100644 --- a/ZeroBanner.php +++ b/ZeroBanner.php @@ -80,6 +80,7 @@ 'remoteExtPath' => $remoteExtPath, 'templates' => array( 'interstitial.hogan' => 'templates/interstitial.hogan', + 'interstitialask.hogan' => 'templates/interstitialask.hogan', 'zeroinfo.hogan' => 'templates/zeroinfo.hogan' ), 'targets' => array( 'mobile' ), @@ -95,6 +96,8 @@ 'zero-accept', 'zero-go-back', 'zero-dont-ask', + 'zero-http', + 'zero-https-http-extended', 'zero-interstitial-title', 'zero-info-title', 'zero-info-intro', diff --git a/i18n/en.json b/i18n/en.json index 1c799f0..69bb801 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -34,6 +34,9 @@ "zero-dont-ask": "Do not warn me about potential charges in the future", "zero-go-back": "Go back", "zero-accept": "Accept", + "zero-http": "Go to http", + "zero-https-http": "Data not free on encrypted https. Free on http", + "zero-https-http-extended": "Your mobile operator supports Wikipedia without data charges on unencrypted http only. You can try browsing on http for free data, but you may get redirected to https if your device does not support it.", "zero-click-to-view-image": "Click to view image of \"$1\"...", "zero-dismiss-notification": "dismiss this notification", "zero-config-name": "Parameter \"$1\" must be an object that maps valid language codes to strings. e.g. { \"en\":\"English\", ... }", diff --git a/i18n/qqq.json b/i18n/qqq.json index 119fa84..1ac9cf2 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -50,6 +50,9 @@ "zero-dont-ask": "Shown next to a checkbox that allows user to always skip the following warning:\n* {{msg-mw|Zero-charge-auth}}", "zero-go-back": "Text for going back (in browser history).\n{{Identical|Go back}}", "zero-accept": "Text for accepting (e.g. charges).\n{{Identical|Accept}}", + "zero-http": "Go to http button label, with http intentionally lowercase due to that being the format in the address bar", + "zero-https-http": "Text concisely explaining encrypted HTTPS is not zero-rated, but HTTP is zero-rated", + "zero-https-http-extended": "Extended explanation about HTTPS not being zero-rated, but HTTP being zero-rated, with an explanation to the user that the user can try to continue to HTTP", "zero-click-to-view-image": "Text for viewing an image link. Parameters:\n* $1 - the alt text of the image that can be viewed\n----\n“Zero rated mobile access only displays text. For images, the value of the \"alt\" [text] attribute is displayed. In case a user may want to the see images, they can click a link, but data charges may then apply. That's the complete context.” [[Thread:Support/About MediaWiki:Zero-rated-mobile-access-click-to-view-image/en/reply|Siebrand – 14:04, 27 February 2012]]", "zero-dismiss-notification": "Text for dismissing banner on top of screen", "zero-config-name": "Aמn error message shown when the \"name\" parameter is invalid. Parameters:\n* $1 - field name\n{{Related|Zeroconfig}}", diff --git a/includes/PageRendering.php b/includes/PageRendering.php index d20b6ad..1cc8ce1 100644 --- a/includes/PageRendering.php +++ b/includes/PageRendering.php @@ -367,7 +367,7 @@ $urlJsBanner = self::getSpecial()->getLocalURL( $query ); $script = Html::linkedScript( $urlJsBanner ); - $noScript = self::renderBannerImgTag( $isFilePage ); + $noScript = self::renderBannerImgTag( $isFilePage, null, $this->isHttps(), $this->isZeroSubdomain() ); // $noScript = // Html::rawElement( 'a', array( 'href' => self::getSpecial()->getLinkURL( 'info=1' ) ), // $noScript ); @@ -395,9 +395,11 @@ * Render <img> tag for the Zero image banner * @param bool $isFilePage * @param string|null $langCode + * @param bool $isHttps + * @param bool $isZeroSubdomain * @return string */ - public static function renderBannerImgTag( $isFilePage, $langCode = null ) { + public static function renderBannerImgTag( $isFilePage, $langCode, $isHttps = false, $isZeroSubdomain = false ) { $query = array( 'zcmd' => 'img-banner' ); if ( $isFilePage ) { $query['zfile'] = '1'; @@ -406,7 +408,39 @@ $query['zlang'] = $langCode; } $url = self::getSpecial()->getLocalURL( $query ); - return Html::rawElement( 'img', array( 'src' => $url ) ); + $html = Html::rawElement( 'img', array( 'src' => $url ) ); + // We know that HTTPS and zerodot are more likely to be risky on <noscript> devices, + // so we give the user a bouncing point. zerodot webroot handles things well enough. + // zerodot not in site list, mdot in sites list: redirects to an mdot page + // zerodot in the site list: redirects the user to a zero-rated zerodot page; + // note that if the user is on zerodot on HTTPS with a <noscript> device, it's + // surprising because there really aren't many entry points into zerodot + // on HTTPS. So kicking the user to HTTP on zerodot should be rare. + // user not on a zero-rated operator: user gets page linking to mdot main page. + // + // If we examine the cases for the if() below, we can reason as follows. + // 1. isHttps, isZeroSubdomain w/ <noscript>: rare. + // 2. isHttps, !isZeroSubdomain w/<noscript> : rare (slightly more common). + // 3. !isHttps, isZeroSubdomain w/<noscript>: rare, but if the user taps the banner user gets landing page + // with a search box or ends up at the main page. No harm done, almost a nice feature. + // 4. !isHttps, !isZeroSubdomain w/<noscript>: this is going to be a 1x1 pixel, so no point linking. + // + // This is simpler than growing the cache per-operator (no!) or putting some sort of complicated + // parameter trying to help us intuit the user's intention + + if ( $isHttps || $isZeroSubdomain ) { + // We probably don't need the project from the (instance) getWikiInfo() method because + // (a) we're not expanding zerodot to sister projects, and + // (b) if the user is on a <noscript> device on an HTTPS connection to a sister + // project, sending the user to zero.wikipedia.org/ could be considered a feature + // of the clickable banner (e.g., if I click here, it gets me to a landing/homepage). + global $wgZeroBannerClusterDomain; + $html = Html::rawElement( 'a', + array( 'href' => "http://zero.wikipedia.$wgZeroBannerClusterDomain/"), + $html ); + + } + return $html; } /** @@ -1105,7 +1139,9 @@ * @return bool */ public function isHttps() { - return $this->getRequest()->getProtocol() === 'https'; + // it's handy to have the $isHttps variable here for local debug simulation of HTTPS + $isHttps = $this->getRequest()->getProtocol() === 'https'; + return $isHttps; } /** diff --git a/includes/ZeroConfig.php b/includes/ZeroConfig.php index 52fa8f1..f1aab04 100644 --- a/includes/ZeroConfig.php +++ b/includes/ZeroConfig.php @@ -194,6 +194,10 @@ return $this->config->enableHttps; } + public function showHttpsDowngrade() { + return $this->config->showHttpsDowngrade; + } + public function ipsetNames() { return $this->config->ipsets; } @@ -319,6 +323,8 @@ $this->test( array( 'configs', $k, 'whitelistedLangs' ), self::getWhitelistedLangsValidator() ); $this->test( array( 'configs', $k, 'proxies' ), self::getProxiesValidator() ); $this->testOptional( array( 'configs', $k, 'enableHttps' ), false, $isBool ); + // If we'll show HTTPS-to-HTTP downgrades, when user is on HTTPS and HTTPS isn't zero-rated + $this->testOptional( array( 'configs', $k, 'showHttpsDowngrade' ), false, $isBool ); $this->testOptional( array( 'configs', $k, 'disableApps' ), false, $isBool ); $this->testOptional( array( 'configs', $k, 'bannerWarning' ), false, $isBool ); } diff --git a/includes/ZeroSpecialPage.php b/includes/ZeroSpecialPage.php index ad8ca1a..c821621 100644 --- a/includes/ZeroSpecialPage.php +++ b/includes/ZeroSpecialPage.php @@ -392,6 +392,9 @@ // $response->header( 'Content-type: application/javascript; charset=UTF-8' ); $banner = ''; + // Even with Apache/Varnish redirects to HTTPS, this is fine, because + // the Special:ZRMA URL from getStartPageUrl does not itself call back + // into this js-banner endpoint, thereby avoiding recursive redirects. if ( $state->isZeroSubdomain() && !$state->getZeroConfig() ) { // If zerodot isn't supported here and the user isn't already on // Special:ZeroRatedMobileAccess, send the user to Special:ZeroRatedMobileAccess @@ -401,15 +404,22 @@ $url = $state->getStartPageUrl( $info[0], $flags ); $banner = "window.location='$url?from='+encodeURIComponent(window.location)"; } else { - $config = $state->getZeroConfig(); - if ( $config && $config->enabled() ) { - $bannerHtml = PageRendering::renderBanner( $state, $config, null, null, $isFilePage ); - $cfg = PageRendering::getJsConfigBlock( $this, $id, $config, (bool)$bannerHtml ); + $config = $state->getZeroConfig( ZeroConfig::ignoreHttps ); + // note we set the ZeroConfig::ignoreHttps flag above, so we need to take that into account for banners + $httpsCausesCharge = $config && $state->isHttps() && !$config->enableHttps(); + $configForBanner = $httpsCausesCharge ? null : $config; + if ( $configForBanner ) { + $bannerHtml = PageRendering::renderBanner( $state, $configForBanner, null, null, $isFilePage ); + $cfg = PageRendering::getJsConfigBlock( $this, $id, $configForBanner, (bool)$bannerHtml ); if ( $bannerHtml ) { $banner = 'document.write(' . Xml::encodeJsVar( $bannerHtml ) . ');'; } } else { - $cfg = PageRendering::getJsConfigBlock( $this, $id, $config, false ); + if ( $config && $config->showHttpsDowngrade() ) { + $banner = 'document.write(' . Xml::encodeJsVar( self::renderHttpsDowngrade() ) . ');'; + } + // Note we use the banner config variable instead of the HTTPS-ignoreing one + $cfg = PageRendering::getJsConfigBlock( $this, $id, $configForBanner, false ); } $banner = $cfg . $banner; } @@ -421,14 +431,29 @@ $lang = $this->getRequest()->getVal( 'zlang' ); // If lang was given (showing in the portal), ignore everything except language $config = - $state->getZeroConfig( $lang ? ( ZeroConfig::ignoreNetwork | ZeroConfig::ignoreDisabled | - ZeroConfig::ignoreHttps | ZeroConfig::ignoreSite ) : 0 ); - $text = PageRendering::getBannerText( $config, $isFilePage, $lang ); + $state->getZeroConfig( $lang + ? ( ZeroConfig::ignoreNetwork | ZeroConfig::ignoreDisabled | + ZeroConfig::ignoreHttps | ZeroConfig::ignoreSite ) + : ZeroConfig::ignoreHttps ); + + // note we set the ZeroConfig::ignoreHttps flag above, so we need to take that into account for banners + $httpsCausesCharge = $config && $state->isHttps() && !$config->enableHttps(); + $text = false; + if ( $httpsCausesCharge && !$lang ) { + $text = PageRendering::getBannerText( $config, $isFilePage, $lang ); + } $banner = false; if ( $text ) { + // implicitly, the configuration was valid $banner = self::createImageBanner( $config->background(), $config->foreground(), $text ); $errors = !$banner; + } elseif ( $httpsCausesCharge && $config->showHttpsDowngrade() ) { + // the configuration would have been valid if it were on http:// cleartext + // TODO: look at the user's cookies to see if the user doesn't care about charges? + // TODO: replicate for JavaScript devices + $banner = self::createImageBanner( 'gainsboro', 'black', $this->msg( 'zero-https-http' )->text(), true ); } elseif ( $state->isZeroSubdomain() ) { + // the configuration only would have been valid if it wasn't on zerodot $host = implode( '.', $state->getWikiInfo() ); $bannerText = $this->msg( 'zero-sorry', $host )->text() . "\n\n"; @@ -455,6 +480,39 @@ return true; } + private static function renderHttpsDowngrade() { + global $wgLang; + + $dismiss = Html::rawElement( + 'button', + array( 'class' => 'notify-close mw-mf-banner-gray', + 'title' => wfMessage( 'zero-dismiss-notification' )->escaped() ), + Html::rawElement( + 'span', + array( 'class' => 'notify-close-x notify-close-x-downgrade mw-mf-banner-gray'), + 'x' + ) + ); + + $banner = Html::rawElement( + 'span', + array( 'id' => 'zero-rated-banner-downgrade', 'class' => 'mw-mf-message mw-mf-banner-gray' ), + Html::rawElement( 'span', + array( + 'id' => 'zero-rli-click', + 'onClick' => "window.ZERODOWNGRADETIMER=setTimeout(function(){window.location='http://'+window.location.hostname+window.location.pathname;},1999)" + ), + wfMessage( 'zero-https-http' )->inLanguage( $wgLang ) + ) + ); + + return Html::rawElement( + 'div', + array( 'class' => 'mw-mf-banner mw-mf-banner-gray' ), + $dismiss . $banner + ); + } + /** * @param WebRequest $request * @param int $min diff --git a/modules/ZeroOverlay.js b/modules/ZeroOverlay.js index c86b9bf..575325d 100644 --- a/modules/ZeroOverlay.js +++ b/modules/ZeroOverlay.js @@ -16,19 +16,22 @@ ProcessDialog.static.title = mw.msg( 'zero-interstitial-title' ); ProcessDialog.static.actions = [ { label: 'X', flags: 'safe' }, - { label: mw.msg( 'zero-accept' ), flags: 'primary', action: 'open' } + { label: mw.msg( options.accept ), flags: 'primary', action: 'open' } ]; ProcessDialog.prototype.initialize = function () { ProcessDialog.super.prototype.initialize.apply( this, arguments ); var tplData = { dontask: mw.msg( 'zero-dont-ask' ), - warning: mw.msg( options.image ? 'zero-file-auth' : 'zero-charge-auth' ) + warning: mw.msg( options.warn ) }; $.extend(tplData, options); - var content = mw.template.get( 'zerobanner', 'interstitial.hogan' ).render( tplData ); + var content = mw.template.get( + 'zerobanner', + options.alwaysAsk ? 'interstitialask.hogan' : 'interstitial.hogan' + ).render( tplData ); this.$body.append( content ); $('#zerodontask').on( 'click', function () { @@ -47,7 +50,8 @@ return ProcessDialog.super.prototype.getTeardownProcess.call( this, data ) .first( function () { if ( window.location.hash.indexOf('#/zerosite') > -1 || - window.location.hash.indexOf('#/zerofile') > -1 ) { + window.location.hash.indexOf('#/zerofile') > -1 || + window.location.hash.indexOf('#/zerodowngrade') > -1 ) { history.replaceState('', document.title, window.location.pathname + location.search); } }, this ); @@ -65,10 +69,18 @@ }; M.router.route( /^\/zerosite\/(.*)/, function( url ) { - return new ZeroOverlay( { url: url, image: false } ); + return new ZeroOverlay( { url: url, warn: 'zero-charge-auth', accept: 'zero-accept' } ); } ); M.router.route( /^\/zerofile\/(.*)/, function( url ) { - return new ZeroOverlay( { url: url, image: true } ); + return new ZeroOverlay( { url: url, warn: 'zero-file-auth', accept: 'zero-accept' } ); + } ); + M.router.route( /^\/zerodowngrade\//, function() { + return new ZeroOverlay( { + url: 'http://' + window.location.hostname + window.location.pathname, + warn: 'zero-https-http-extended', + alwaysAsk: true, + accept: 'zero-http' + } ); } ); M.define( 'ZeroOverlay', ZeroOverlay ); diff --git a/modules/banner.css b/modules/banner.css index 704be35..10103be 100644 --- a/modules/banner.css +++ b/modules/banner.css @@ -19,8 +19,14 @@ white-space: normal !important; font-family: Tahoma, sans-serif; } +.mw-mf-banner-gray { + color: #000000 !important; + background: #DCDCDC !important; + font-weight: normal; +} .mw-mf-banner .mw-mf-message, -.mw-mf-banner button { +.mw-mf-banner button, +.mw-mf-banner-gray button { padding-top: 10px; padding-bottom: 10px; } @@ -54,6 +60,11 @@ width: 1.3em; line-height: 0.6; } + +.mw-mf-banner span.notify-close-x-downgrade { + border: 2px solid #000000 !important; +} + .mw-mf-banner p { line-height: normal; } diff --git a/modules/interstitial.js b/modules/interstitial.js index 7272a36..0c57e80 100644 --- a/modules/interstitial.js +++ b/modules/interstitial.js @@ -62,6 +62,15 @@ } ); } + if ( config ) { + $( '#zero-rated-banner-downgrade, #zero-rli-click' ).on( 'click', function ( ev ) { + ev.preventDefault(); + clearTimeout( window.ZERODOWNGRADETIMER ); + M.require( 'ZeroOverlay' ); + window.location.hash = '#/zerodowngrade/'; + } ); + } + // Disable other click event handlers for images/thumbnails, for example, the // mobile media viewer. // FIXME: Figure out a less fragile way to handle this diff --git a/templates/interstitialask.hogan b/templates/interstitialask.hogan new file mode 100644 index 0000000..452e812 --- /dev/null +++ b/templates/interstitialask.hogan @@ -0,0 +1,3 @@ +<div class="content"> + <p>{{warning}}</p> +</div> \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/184995 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I91aa173f915310cba5de75866b89059d9b8e3d91 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/ZeroBanner Gerrit-Branch: master Gerrit-Owner: Yurik <yu...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits