jenkins-bot has submitted this change and it was merged.

Change subject: Hide charge-inducing features. Intercept link interstitials 
sooner.
......................................................................


Hide charge-inducing features. Intercept link interstitials sooner.

* HTTPS can cause charges. This hides obvious HTTPS chrome until safe.
* Rewritten hyperlinks normally result in a server roundtrip upon click.
* This patch tries to render a client-side interstitial opportunistically.
* However, without binding to all click events, some clicks will default to the 
server.
* For hyperlinks that look like Wikipedia language switches, a last-ditch 
server check is allowed.
* Eventually, HTTPS will be supported via IP zero-rating. At that point, this 
code should be updated.

Change-Id: Icd8c9140e3a22de60e148670f7bbc67a356950d8
---
M ZeroRatedMobileAccess.php
M includes/ApiZeroConfig.php
A modules/ZeroOverlay.js
A modules/carrierConfig.js
A modules/interstitial.js
A modules/redux.js
A modules/redux.less
A templates/interstitial.html
8 files changed, 199 insertions(+), 3 deletions(-)

Approvals:
  JGonera: Looks good to me, but someone else must approve
  Yurik: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/ZeroRatedMobileAccess.php b/ZeroRatedMobileAccess.php
index 32329d5..1bd428f 100644
--- a/ZeroRatedMobileAccess.php
+++ b/ZeroRatedMobileAccess.php
@@ -60,15 +60,27 @@
        'localBasePath' => $localBasePath,
        'remoteExtPath' => $remoteExtPath,
        'targets' => array( 'mobile' ),
-       'styles' => array( 'modules/banner.css' ),
+       'styles' => array( 'modules/banner.css', 'modules/redux.less' ),
+);
+
+$wgResourceModules['mobile.zero.templates'] = array(
+       'localBasePath' => $localBasePath,
+       'localTemplateBasePath' => $localBasePath . '/templates',
+       'templates' => array( 'interstitial' ),
+       'class' => 'MFResourceLoaderModule',
 );
 
 $wgResourceModules['mobile.zero.scripts'] = array(
-       'dependencies' => array( 'mobile.startup' ),
+       'dependencies' => array( 'mobile.stable', 'mobile.zero.styles', 
'mobile.zero.templates' ),
        'localBasePath' => $localBasePath,
        'remoteExtPath' => $remoteExtPath,
        'targets' => array( 'mobile' ),
-       'scripts' => array( 'modules/banner.js' ),
+       'scripts' => array( 'modules/ZeroOverlay.js', 
'modules/carrierConfig.js', 'modules/banner.js', 'modules/interstitial.js', 
'modules/redux.js' ),
+       'messages' => array(
+               'zero-rated-mobile-access-charge-auth',
+               'zero-rated-mobile-access-banner-text-data-charges-yes',
+               'zero-rated-mobile-access-banner-text-data-charges-no'
+       )
 );
 
 $wgResourceModules['zero.config'] = array(
diff --git a/includes/ApiZeroConfig.php b/includes/ApiZeroConfig.php
index 08fea8c..2db0062 100644
--- a/includes/ApiZeroConfig.php
+++ b/includes/ApiZeroConfig.php
@@ -36,6 +36,7 @@
                                        $result->addValue( null, 'showImages', 
$config['showImages'] );
                                        $result->addValue( null, 
'whitelistedLangs', $config['whitelistedLangs'] );
                                        $result->addValue( null, 'enableHttps', 
$config['enableHttps'] );
+                                       $result->addValue( null, 'sites', 
$config['sites'] );
                                } else {
                                        $result->addValue( null, 'enabled', 
false );
                                }
diff --git a/modules/ZeroOverlay.js b/modules/ZeroOverlay.js
new file mode 100644
index 0000000..7d2dbbb
--- /dev/null
+++ b/modules/ZeroOverlay.js
@@ -0,0 +1,19 @@
+( function( M ) {
+    var Overlay = M.require( 'Overlay' ),
+        ZeroOverlay = Overlay.extend( {
+            template: M.template.get( 'interstitial' ),
+            closeOnBack: true,
+            defaults: {
+                yes: mw.msg( 
'zero-rated-mobile-access-banner-text-data-charges-yes' ),
+                no: mw.msg( 
'zero-rated-mobile-access-banner-text-data-charges-no' ),
+                warning: mw.msg( 'zero-rated-mobile-access-charge-auth' )
+            }
+        } );
+
+    M.router.route( /^interstitial\/(.*)/, function( url ) {
+        new ZeroOverlay( { url: url } ).show();
+    } );
+
+    M.define( 'ZeroOverlay', ZeroOverlay );
+
+}( mw.mobileFrontend ) );
\ No newline at end of file
diff --git a/modules/carrierConfig.js b/modules/carrierConfig.js
new file mode 100644
index 0000000..0b3cc59
--- /dev/null
+++ b/modules/carrierConfig.js
@@ -0,0 +1,4 @@
+( function( M ) {
+    var api = M.require( 'api' );
+    M.define( 'carrierConfig', api.get( { action: 'zeroconfig', type: 'config' 
} ) );
+}( mw.mobileFrontend ) );
\ No newline at end of file
diff --git a/modules/interstitial.js b/modules/interstitial.js
new file mode 100644
index 0000000..e59cc6d
--- /dev/null
+++ b/modules/interstitial.js
@@ -0,0 +1,86 @@
+( function ( M, $ ) {
+    var httpsProxyingEnhanced = false, /* whether Varnish handles HTTPS nicely 
*/
+        isHttpsFree = false, /* declaration for whether carrier's config and 
Varnish make it free  */
+        zeroRe = /renderZeroRatedRedirect=true/, /* legacy interstitial link 
indicator */
+        fromRe = /./, /* legacy interstitial link FROM indicator (wildcard) */
+        toRe = /[&?]returnto=([^&]+)/, /* legacy interstitial link TO 
indicator */
+        zeroReModern = /Special:ZeroRatedMobileAccess(.+)$/, /* new 
interstitial link indicator */
+        fromReModern = /[&?]from=([^&]+)/, /* new interstitial FROM indicator 
*/
+        toReModern = /[&?]to=([^&]+)/, /* new interstitial TO indicator */
+        wikipediaRe = 
/^(http:\/\/|https:\/\/|\/\/)([^.]+)\.([^.]+\.[^.]+)\.org/, /* hypothetically, 
a Wikipedia (actually Wikimedia) URL (more restrictions below) */
+        isCurrentlyHttps = window.location.protocol.toLowerCase() === 
'https:', /* whether user is on an insecure http:// page */
+        areImagesFree = false,
+        ZeroOverlay = M.require( 'ZeroOverlay' ),
+        carrierConf = M.require( 'carrierConfig' );
+
+    $( function () {
+
+        carrierConf.done( function( conf ) {
+            var allLangsFree = false, /* variable declaration saying we don't 
which languages are free yet */
+                freeLangs = [], /* variable declaration of free languages, 
which we don't know yet */
+                sites = []; /* variable declaration of whether mdot or zerodot 
are free, which we don't know yet */
+
+            if ( conf.enableHttps !== undefined && conf.enableHttps === true ) 
{
+                isHttpsFree = httpsProxyingEnhanced;
+            }
+
+            if ( conf.whitelistedLangs ) {
+                if ( conf.whitelistedLangs.length === 0 ) {
+                    allLangsFree = true;
+                } else {
+                    freeLangs = conf.whitelistedLangs;
+                }
+            }
+
+            if ( conf.sites ) {
+                sites = conf.sites;
+            }
+
+
+            if ( conf.showImages ) {
+                areImagesFree = true;
+            }
+
+            $( 'a' ).on( 'click', function ( ev ) {
+                var $eventTarget = $( this ), /* onclick element */
+                    dest = $eventTarget.prop( 'href' ), /* actual url of 
hyperlink */
+                    destParts = [], /* declaration to contain split up URL */
+                    match = toRe.exec( dest ), /* whether this link looks 
legacy redirecty */
+                    matchModern = toReModern.exec( dest ), /* whether this 
link looks redirecty on the new system */
+                    regexesPassed = dest && zeroRe.test( dest ) && 
fromRe.test( dest ) && match !== null, /* whether the link is legacy formatted 
for interstitials */
+                    regexesPassedModern = dest && zeroReModern.test( dest ) && 
fromReModern.test( dest ) && matchModern !== null, /* whether the link is 
modernly formatted for interstitials */
+                    extractedUrl = '', /* variable declaration to eventually 
extract the appropriate destination URL */
+                    subdomainWhitelisted = false, /* declaration that until 
proven otherwise destination links are unsafe */
+                    uriSchemeSafe = false, /* declaration that until proven 
otherwise the destination link's scheme is unsafe */
+                    interstitialRequired = true; /* interstitial is necessary 
until proven otherwise */
+
+                if ( regexesPassed || regexesPassedModern ) {
+                    extractedUrl = regexesPassedModern ? matchModern[1] : 
match[1];
+                    destParts = wikipediaRe.exec( extractedUrl );
+                    if ( destParts !== null ) {
+
+                        subdomainWhitelisted = ( allLangsFree || 
freeLangs.indexOf( destParts[2] ) !== -1 ) &&
+                            sites.indexOf( destParts[3] ) !== -1 ;
+
+                        uriSchemeSafe = destParts[1] !== 'https://' || 
isHttpsFree || isCurrentlyHttps;
+
+                        if ( subdomainWhitelisted && uriSchemeSafe ) {
+                            interstitialRequired = false;
+                        }
+
+                    }
+
+                    if ( areImagesFree && $eventTarget.hasClass( 'image' ) && 
extractedUrl.test( /\/[^\/]]/ ) ) {
+                        interstitialRequired = false;
+                    }
+
+                    if ( interstitialRequired ) {
+                        window.location.hash = '#interstitial/' + 
decodeURIComponent( extractedUrl );
+                        ev.preventDefault();
+                    }
+                }
+            } );
+
+        } );
+    } );
+}( mw.mobileFrontend, jQuery ) );
\ No newline at end of file
diff --git a/modules/redux.js b/modules/redux.js
new file mode 100644
index 0000000..f5d178a
--- /dev/null
+++ b/modules/redux.js
@@ -0,0 +1,53 @@
+( function ( M, $ ) {
+    var httpsProxyingEnhanced = false,
+        isMdot = document.location.hostname.toLowerCase().search( 
/\.m\.wikipedia\.org$/ ) !== -1,
+        isCleartext = document.location.protocol.toLowerCase() === 'http:',
+        ZeroOverlay = M.require( 'ZeroOverlay' );
+
+    $( function () {
+        var carrierConf = M.require( 'carrierConfig' ),
+            conf = {},
+            isHttpsFree = false;
+
+        carrierConf.done( function( conf ) {
+
+            if ( conf.enableHttps !== undefined && conf.enableHttps === true ) 
{
+                isHttpsFree = httpsProxyingEnhanced;
+            }
+
+            if ( !isCleartext && !isHttpsFree ) {
+                /* we can't hide by default in CSS b/c of feature phones */
+                $( '#zero-rated-banner' ).hide();
+            }
+
+            if ( isCleartext && !isHttpsFree ) {
+                /* TODO: once history page not Desktop-y, remove this */
+                $( '#mw-mf-last-modified' ).on( 'click', function ( ev ) {
+                    var that = this;
+                    window.location.hash = '#interstitial/' + $( that ).attr( 
'href' );
+                    ev.preventDefault();
+                } );
+            }
+
+            if ( isMdot ) {
+                /* CSS isn't working for this element, and !important breaks 
stuff */
+                $( '.icon-settings' ).show();
+            }
+
+            if ( isHttpsFree ) {
+                if ( isMdot ) {
+                    /*
+                        FIXME: jdlrobson in change 83133 - "Maybe 
MobileFrontend can make this
+                        easier by marking these links with a shared class so 
you can do something
+                        like $( '.httpsLink' ).show();"
+                    */
+                    $( 
'.icon-uploads,.icon-watchlist,.icon-loginout,#ca-upload' ).show();
+
+                }
+
+                $( '#page-actions' ).show();
+            }
+
+        } );
+    } );
+}( mw.mobileFrontend, jQuery ) );
\ No newline at end of file
diff --git a/modules/redux.less b/modules/redux.less
new file mode 100644
index 0000000..8df872e
--- /dev/null
+++ b/modules/redux.less
@@ -0,0 +1,14 @@
+#page-actions,
+.icon-watchlist,
+.icon-settings,
+.icon-loginout,
+.icon-nearby,
+.icon-uploads,
+#ca-upload,
+/* next line is on settings page */
+.mw-mf-settings-description,
+/* next two lines for the settings page (requires good CSS support, though) */
+.mw-mf-settings > ul:nth-child(2) > li:nth-child(2),
+.mw-mf-settings > ul:nth-child(2) > li:nth-child(4)  {
+    display:none !important;
+}
\ No newline at end of file
diff --git a/templates/interstitial.html b/templates/interstitial.html
new file mode 100644
index 0000000..3c6bcab
--- /dev/null
+++ b/templates/interstitial.html
@@ -0,0 +1,7 @@
+<div class="header mw-mf-banner mw-mf-banner-undismissable">
+{{warning}}
+</div>
+<div class="button-bar button-bar-centered buttonBar">
+    <a href="{{url}}" class="button">{{yes}}</a>
+    <button class="cancel inline">{{no}}</button>
+</div>
\ No newline at end of file

-- 
To view, visit https://gerrit.wikimedia.org/r/83133
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Icd8c9140e3a22de60e148670f7bbc67a356950d8
Gerrit-PatchSet: 39
Gerrit-Project: mediawiki/extensions/ZeroRatedMobileAccess
Gerrit-Branch: master
Gerrit-Owner: Dr0ptp4kt <[email protected]>
Gerrit-Reviewer: Awjrichards <[email protected]>
Gerrit-Reviewer: Dfoy <[email protected]>
Gerrit-Reviewer: Dr0ptp4kt <[email protected]>
Gerrit-Reviewer: JGonera <[email protected]>
Gerrit-Reviewer: Jdlrobson <[email protected]>
Gerrit-Reviewer: Kaldari <[email protected]>
Gerrit-Reviewer: Yurik <[email protected]>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to