Ori.livneh has uploaded a new change for review. https://gerrit.wikimedia.org/r/242346
Change subject: Migrate to a terser cookie name and format ...................................................................... Migrate to a terser cookie name and format * Rename cookie from 'centralnotice_buckets_by_campaign' to 'CN'. * Store start as offset from UNIX time 1400000000 (March 2014). * Store end as offset from start. * Instead of JSON, use this format: 'NAME!START!END!VALUE'. Separate campaigns with '*'. Before: centralnotice_buckets_by_campaign=%257B%2522WikiConference_USA%2522 %253A%257B%2522val%2522%253A0%252C%2522start%2522%253A1439942400%25 2C%2522end%2522%253A1443672000%257D%257D After: CN=WikiConference_USA!39942400!3729600 Bug: T110353 Change-Id: I2b39d153bebce90b47a901be24fbb0c0f605e53d (cherry picked from commit 721bf73a1c630ec17625667783a50a9f87c03148) --- M resources/subscribing/ext.centralNotice.display.bucketer.js M resources/subscribing/ext.centralNotice.startUp.js M tests/qunit/subscribing/ext.centralNotice.display.tests.js 3 files changed, 101 insertions(+), 37 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/CentralNotice refs/changes/46/242346/1 diff --git a/resources/subscribing/ext.centralNotice.display.bucketer.js b/resources/subscribing/ext.centralNotice.display.bucketer.js index 3b1e41e..a930246 100644 --- a/resources/subscribing/ext.centralNotice.display.bucketer.js +++ b/resources/subscribing/ext.centralNotice.display.bucketer.js @@ -1,35 +1,102 @@ /** * Storage, retrieval and other processing of buckets. Provides * cn.internal.bucketer. + * + * Bucket assignments are stored in a cookie named simply 'CN', to maximize + * concision. It consists of '*'-separated campaigns, each of which is made up + * for '!'-separated fields. The format is: + * + * NAME!START!END!VALUE[*NAME!START!END!VALUE..] + * + * - Start is stored as a second offset from UNIX timestamp 1400000000 + * (March 2014). + * - End is stored as second offset from start. + * + * For example: + * + * CN=WikiConference_USA!39942400!3729600 + * + * ...would be deserialized to: + * + * { WikiConference_USA: { start: 1439942400, end: 1443672000 } } + * */ ( function ( $, mw ) { - var BUCKET_COOKIE_NAME = 'centralnotice_buckets_by_campaign', + // Name of the old (pre-I2b39d153b) cookie for CentralNotice buckets. + // Its value is a JSON-encoded object, mapping campaign names to plain + // objects with 'start', 'end', and 'val' parameters. + var LEGACY_COOKIE = 'centralnotice_buckets_by_campaign', // Bucket objects by campaign; properties are campaign names. // Retrieved from bucket cookie, if available. - buckets, + buckets = null, // The campaign we're working with. campaign = null; /** + * Escape '*' and '!' in a campaign name to make it safe for serialization. + */ + function escapeCampaignName( name ) { + return name.replace( /[*!]/g, function ( match ) { + return '&#' + match.charCodeAt( 0 ); + } ); + } + + /** + * Decode any escaped '*' and '!' characters in a serialized campaign name. + */ + function decodeCampaignName( name ) { + return name.replace( /&#(33|42)/, function ( match, $1 ) { + return String.fromCharCode( $1 ); + } ); + } + + /** * Attempt to get buckets from the bucket cookie. If there is no - * bucket cookie, set buckets to an empty object. + * bucket cookie, check for a 'legacy cookie' (i.e., a cookie with + * the name and format used prior to I2b39d153b); if there is one, + * migrate it to the newer cookie format. If neither cookie exists, + * set buckets to an empty object. */ function loadBuckets() { - var cookieVal = $.cookie( BUCKET_COOKIE_NAME ); + var cookieVal = $.cookie( 'CN' ); - if ( cookieVal ) { - try { - buckets = JSON.parse( cookieVal ); - } catch ( e ) { - // Very likely a syntax error due to corrupt cookie contents - buckets = {}; + buckets = {}; + + if ( !cookieVal ) { + // Prior to I2b39d153be, the campaign cookie had a different + // (longer) name and used JSON encoding. If the user has such + // a cookie, migrate it to the new format. + cookieVal = $.cookie( LEGACY_COOKIE ); + if ( cookieVal ) { + $.removeCookie( LEGACY_COOKIE, { path: '/' } ); + try { + $.extend( buckets, JSON.parse( cookieVal ) ); + } catch ( e ) {} + if ( !$.isEmptyObject( buckets ) ) { + storeBuckets(); + } } - } else { - buckets = {}; + return; } + + $.each( cookieVal.split( '*' ), function ( idx, strBucket ) { + var parts = strBucket.split( '!' ), + key = decodeCampaignName( parts[0] ), + start = parseInt( parts[1], 10 ) + 14e8, + end = start + parseInt( parts[2], 10 ), + val = parts[3]; + + if ( key && start && end && val !== undefined ) { + buckets[ key ] = { + start: start, + end: end, + val: val + }; + } + } ); } /** @@ -37,29 +104,27 @@ * after the all the buckets it contains do. */ function storeBuckets() { - var now = new Date(), - latestDate, - campaignName, bucketEndDate; + var expires = Math.ceil( ( new Date() ) / 1000 ), + cookieVal = $.map( buckets, function ( opts, key ) { + var parts = [ + escapeCampaignName( key ), + Math.floor( opts.start - 14e8 ), + Math.ceil( opts.end - opts.start ), + opts.val + ]; - // Cycle through the buckets to find the latest end date - latestDate = now; - for ( campaignName in buckets ) { + if ( opts.end > expires ) { + expires = Math.ceil( opts.end ); + } - bucketEndDate = new Date(); - bucketEndDate.setTime( buckets[campaignName].end * 1000 ); - - if ( bucketEndDate > latestDate ) { - latestDate = bucketEndDate; - } - } - - latestDate.setDate( latestDate.getDate() + 1 ); + return parts.join( '!' ); + } ).join( '*' ); // Store the buckets in the cookie - $.cookie( BUCKET_COOKIE_NAME, - JSON.stringify( buckets ), - { expires: latestDate, path: '/' } - ); + $.cookie( 'CN', cookieVal, { + expires: new Date( expires * 1000 ), + path: '/', + } ); } /** @@ -206,5 +271,4 @@ storeBuckets(); } }; - -} )( jQuery, mediaWiki ); \ No newline at end of file +} )( jQuery, mediaWiki ); diff --git a/resources/subscribing/ext.centralNotice.startUp.js b/resources/subscribing/ext.centralNotice.startUp.js index 5985f13..ca9f745 100644 --- a/resources/subscribing/ext.centralNotice.startUp.js +++ b/resources/subscribing/ext.centralNotice.startUp.js @@ -84,4 +84,4 @@ cn.chooseAndMaybeDisplay(); -} )( jQuery, mediaWiki ); \ No newline at end of file +} )( jQuery, mediaWiki ); diff --git a/tests/qunit/subscribing/ext.centralNotice.display.tests.js b/tests/qunit/subscribing/ext.centralNotice.display.tests.js index 3a3704b..e3b89ac 100644 --- a/tests/qunit/subscribing/ext.centralNotice.display.tests.js +++ b/tests/qunit/subscribing/ext.centralNotice.display.tests.js @@ -4,7 +4,7 @@ var realAjax = $.ajax, realWindowGeo = window.Geo, realGeoIP = mw.geoIP, - realBucketCookie = $.cookie( 'centralnotice_buckets_by_campaign' ), + realBucketCookie = $.cookie( 'CN' ), realHideCookie = $.cookie( 'centralnotice_hide_fundraising' ), bannerData = { bannerName: 'test_banner', @@ -133,7 +133,7 @@ $( '#siteNotice' ).remove(); $.removeCookie( 'centralnotice_hide_fundraising', { path: '/' } ); - $.removeCookie( 'centralnotice_buckets_by_campaign', { path: '/' } ); + $.removeCookie( 'CN', { path: '/' } ); // Suppress background calls $.ajax = function() { return $.Deferred(); }; @@ -157,7 +157,7 @@ $.ajax = realAjax; mw.geoIP = realGeoIP; $.cookie( 'centralnotice_hide_fundraising', realHideCookie, { path: '/' } ); - $.cookie( 'centralnotice_buckets_by_campaign', realBucketCookie, { path: '/' } ); + $.cookie( 'CN', realBucketCookie, { path: '/' } ); mw.centralNotice.internal.state.data = {}; mw.centralNotice.internal.state.campaign = null; mw.centralNotice.internal.state.banner = null; -- To view, visit https://gerrit.wikimedia.org/r/242346 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I2b39d153bebce90b47a901be24fbb0c0f605e53d Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/CentralNotice Gerrit-Branch: wmf_deploy Gerrit-Owner: Ori.livneh <o...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits