Robmoen has submitted this change and it was merged.

Change subject: Implement mediawiki.cookie module
......................................................................


Implement mediawiki.cookie module

Provides functonality similar to WebRequest#getCookie and WebResponse#setcookie.

Wraps $.cookie and automatically takes care of wgCookiePrefix etc.

Bug: 49156
Change-Id: I217ef258aecf1acd335e2cea56ae08b22541c7d4
Co-Author: Matthew Flaschen <mflasc...@wikimedia.org>
Co-Author: Timo Tijhof <krinklem...@gmail.com>
---
M RELEASE-NOTES-1.23
M includes/resourceloader/ResourceLoaderStartUpModule.php
M maintenance/jsduck/categories.json
M resources/Resources.php
A resources/src/mediawiki/mediawiki.cookie.js
M tests/qunit/QUnitTestResources.php
A tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js
7 files changed, 317 insertions(+), 2 deletions(-)

Approvals:
  Robmoen: Looks good to me, approved



diff --git a/RELEASE-NOTES-1.23 b/RELEASE-NOTES-1.23
index 231903c..622d96e 100644
--- a/RELEASE-NOTES-1.23
+++ b/RELEASE-NOTES-1.23
@@ -156,6 +156,9 @@
   in StartProfiler.php instead of using this.
 * (bug 63444) Made it possible to change the indent string (default: 4 spaces)
   used by FormatJson::encode().
+* (bug 49156) Added the mediawiki.cookie ResourceLoader module, which wraps
+  jQuery.cookie so that getting/setting a cookie is syntactically and 
functionally
+  similar to using the WebRequest#getCookie/WebResponse#setcookie methods.
 
 === Bug fixes in 1.23 ===
 * (bug 41759) The "updated since last visit" markers (on history pages, recent
diff --git a/includes/resourceloader/ResourceLoaderStartUpModule.php 
b/includes/resourceloader/ResourceLoaderStartUpModule.php
index d0f0541..d4359c5 100644
--- a/includes/resourceloader/ResourceLoaderStartUpModule.php
+++ b/includes/resourceloader/ResourceLoaderStartUpModule.php
@@ -48,7 +48,8 @@
                        $wgVariantArticlePath, $wgActionPaths, $wgVersion,
                        $wgEnableAPI, $wgEnableWriteAPI, $wgDBname,
                        $wgSitename, $wgFileExtensions, $wgExtensionAssetsPath,
-                       $wgCookiePrefix, $wgResourceLoaderMaxQueryLength,
+                       $wgCookiePrefix, $wgCookieDomain, $wgCookiePath,
+                       $wgCookieExpiration, $wgResourceLoaderMaxQueryLength,
                        $wgResourceLoaderStorageEnabled, 
$wgResourceLoaderStorageVersion,
                        $wgSearchType;
 
@@ -104,6 +105,9 @@
                        'wgExtensionAssetsPath' => $wgExtensionAssetsPath,
                        // MediaWiki sets cookies to have this prefix by default
                        'wgCookiePrefix' => $wgCookiePrefix,
+                       'wgCookieDomain' => $wgCookieDomain,
+                       'wgCookiePath' => $wgCookiePath,
+                       'wgCookieExpiration' => $wgCookieExpiration,
                        'wgResourceLoaderMaxQueryLength' => 
$wgResourceLoaderMaxQueryLength,
                        'wgCaseSensitiveNamespaces' => $caseSensitiveNamespaces,
                        'wgLegalTitleChars' => 
Title::convertByteClassToUnicodeClass( Title::legalChars() ),
diff --git a/maintenance/jsduck/categories.json 
b/maintenance/jsduck/categories.json
index 2d45645..93abf87 100644
--- a/maintenance/jsduck/categories.json
+++ b/maintenance/jsduck/categories.json
@@ -25,7 +25,8 @@
                                        "mw.Notification_",
                                        "mw.user",
                                        "mw.util",
-                                       "mw.plugin.*"
+                                       "mw.plugin.*",
+                                       "mw.cookie"
                                ]
                        },
                        {
diff --git a/resources/Resources.php b/resources/Resources.php
index 668dc14..767c9cf 100644
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -240,6 +240,7 @@
        'jquery.colorUtil' => array(
                'scripts' => 'resources/src/jquery/jquery.colorUtil.js',
        ),
+       // Use mediawiki.cookie in new code, rather than jquery.cookie.
        'jquery.cookie' => array(
                'scripts' => 'resources/lib/jquery/jquery.cookie.js',
                'targets' => array( 'desktop', 'mobile' ),
@@ -911,6 +912,12 @@
                'position' => 'top', // For $wgPreloadJavaScriptMwUtil
                'targets' => array( 'desktop', 'mobile' ),
        ),
+       'mediawiki.cookie' => array(
+               'scripts' => 'resources/src/mediawiki/mediawiki.cookie.js',
+               'dependencies' => array(
+                       'jquery.cookie',
+               ),
+       ),
 
        /* MediaWiki Action */
 
diff --git a/resources/src/mediawiki/mediawiki.cookie.js 
b/resources/src/mediawiki/mediawiki.cookie.js
new file mode 100644
index 0000000..657edf3
--- /dev/null
+++ b/resources/src/mediawiki/mediawiki.cookie.js
@@ -0,0 +1,126 @@
+( function ( mw, $ ) {
+       'use strict';
+
+       /**
+        * Provides an API for getting and setting cookies that is
+        * syntactically and functionally similar to the server-side cookie
+        * API (`WebRequest#getCookie` and `WebResponse#setcookie`).
+        *
+        * @author Sam Smith <samsm...@wikimedia.org>
+        * @author Matthew Flaschen <mflasc...@wikimedia.org>
+        * @author Timo Tijhof <krinklem...@gmail.com>
+        *
+        * @class mw.cookie
+        * @singleton
+        */
+       mw.cookie = {
+
+               /**
+                * Sets or deletes a cookie.
+                *
+                * While this is natural in JavaScript, contrary to 
`WebResponse#setcookie` in PHP, the
+                * default values for the `options` properties only apply if 
that property isn't set
+                * already in your options object (e.g. passing `{ secure: null 
}` or `{ secure: undefined }`
+                * overrides the default value for `options.secure`).
+                *
+                * @param {string} key
+                * @param {string|null} value Value of cookie. If `value` is 
`null` then this method will
+                *   instead remove a cookie by name of `key`.
+                * @param {Object|Date} [options] Options object, or expiry date
+                * @param {Date|boolean} [options.expires=wgCookieExpiration] 
The expiry date of the cookie.
+                *
+                *   Default cookie expiration is based on 
`wgCookieExpiration`.  If `wgCookieExpiration` is
+                *   0, a session cookie is set (expires when the browser is 
closed). For non-zero values of
+                *   `wgCookieExpiration`, the cookie expires 
`wgCookieExpiration` seconds from now.
+                *
+                *   If options.expires is null, then a session cookie is set.
+                * @param {string} [options.prefix=wgCookiePrefix] The prefix 
of the key
+                * @param {string} [options.domain=wgCookieDomain] The domain 
attribute of the cookie
+                * @param {string} [options.path=wgCookiePath] The path 
attribute of the cookie
+                * @param {boolean} [options.secure=false] Whether or not to 
include the secure attribute.
+                *   (Does **not** use the wgCookieSecure configuration 
variable)
+                */
+               set: function ( key, value, options ) {
+                       var config, defaultOptions, date;
+
+                       // wgCookieSecure is not used for now, since 'detect' 
could not work with
+                       // ResourceLoaderStartUpModule, as module cache is not 
fragmented by protocol.
+                       config = mw.config.get( [
+                               'wgCookiePrefix',
+                               'wgCookieDomain',
+                               'wgCookiePath',
+                               'wgCookieExpiration'
+                       ] );
+
+                       defaultOptions = {
+                               prefix: config.wgCookiePrefix,
+                               domain: config.wgCookieDomain,
+                               path: config.wgCookiePath,
+                               secure: false
+                       };
+
+                       // Options argument can also be a shortcut for the 
expiry
+                       // Expiry can be a Date or null
+                       if ( $.type( options ) !== 'object' ) {
+                               // Also takes care of options = undefined, in 
which case we also don't need $.extend()
+                               defaultOptions.expires = options;
+                               options = defaultOptions;
+                       } else {
+                               options = $.extend( defaultOptions, options );
+                       }
+
+                       // $.cookie makes session cookies when expiry is 
omitted,
+                       // however our default is to expire wgCookieExpiration 
seconds from now.
+                       // Note: If wgCookieExpiration is 0, that is considered 
a special value indicating
+                       // all cookies should be session cookies by default.
+                       if ( options.expires === undefined && 
config.wgCookieExpiration !== 0 ) {
+                               date = new Date();
+                               date.setTime( Number( date ) + ( 
config.wgCookieExpiration * 1000 ) );
+                               options.expires = date;
+                       } else if ( options.expires === null ) {
+                               // $.cookie makes a session cookie when expires 
is omitted
+                               delete options.expires;
+                       }
+
+                       // Process prefix
+                       key = options.prefix + key;
+                       delete options.prefix;
+
+                       // Process value
+                       if ( value !== null ) {
+                               value = String( value );
+                       }
+
+                       // Other options are handled by $.cookie
+                       $.cookie( key, value, options );
+               },
+
+               /**
+                * Gets the value of a cookie.
+                *
+                * @param {string} key
+                * @param {string} [prefix=wgCookiePrefix] The prefix of the 
key. If `prefix` is
+                *   `undefined` or `null`, then `wgCookiePrefix` is used
+                * @param {Mixed} [defaultValue=null]
+                * @return {string} If the cookie exists, then the value of the
+                *   cookie, otherwise `defaultValue`
+                */
+               get: function ( key, prefix, defaultValue ) {
+                       var result;
+
+                       if ( prefix === undefined || prefix === null ) {
+                               prefix = mw.config.get( 'wgCookiePrefix' );
+                       }
+
+                       // Was defaultValue omitted?
+                       if ( arguments.length < 3 ) {
+                               defaultValue = null;
+                       }
+
+                       result = $.cookie( prefix + key );
+
+                       return result !== null ? result : defaultValue;
+               }
+       };
+
+} ( mediaWiki, jQuery ) );
diff --git a/tests/qunit/QUnitTestResources.php 
b/tests/qunit/QUnitTestResources.php
index e861967..f48397f 100644
--- a/tests/qunit/QUnitTestResources.php
+++ b/tests/qunit/QUnitTestResources.php
@@ -76,6 +76,7 @@
                        
'tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js',
                        
'tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js',
                        
'tests/qunit/suites/resources/mediawiki/mediawiki.cldr.test.js',
+                       
'tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js',
                ),
                'dependencies' => array(
                        'jquery.accessKeyLabel',
@@ -107,6 +108,7 @@
                        'mediawiki.special.recentchanges',
                        'mediawiki.language',
                        'mediawiki.cldr',
+                       'mediawiki.cookie',
                        'test.mediawiki.qunit.testrunner',
                ),
        )
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js 
b/tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js
new file mode 100644
index 0000000..c9653da
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js
@@ -0,0 +1,172 @@
+( function ( mw, $ ) {
+
+       var NOW = 9012, // miliseconds
+               DEFAULT_DURATION = 5678, // seconds
+               expiryDate = new Date();
+
+       expiryDate.setTime( NOW + ( DEFAULT_DURATION * 1000 ) );
+
+       QUnit.module( 'mediawiki.cookie', QUnit.newMwEnvironment( {
+               setup: function () {
+                       this.stub( $, 'cookie' ).returns( null );
+
+                       this.sandbox.useFakeTimers( NOW );
+               },
+               config: {
+                       wgCookiePrefix: 'mywiki',
+                       wgCookieDomain: 'example.org',
+                       wgCookiePath: '/path',
+                       wgCookieExpiration: DEFAULT_DURATION
+               }
+       } ) );
+
+       QUnit.test( 'set( key, value )', 7, function ( assert ) {
+               var call;
+
+               // Simple case
+               mw.cookie.set( 'foo', 'bar' );
+
+               call = $.cookie.lastCall.args;
+               assert.strictEqual( call[ 0 ], 'mywikifoo' );
+               assert.strictEqual( call[ 1 ], 'bar' );
+               assert.deepEqual( call[ 2 ], {
+                       expires: expiryDate,
+                       domain: 'example.org',
+                       path: '/path',
+                       secure: false
+               } );
+
+               mw.cookie.set( 'foo', null );
+               call = $.cookie.lastCall.args;
+               assert.strictEqual( call[ 1 ], null, 'null removes cookie' );
+
+               mw.cookie.set( 'foo', undefined );
+               call = $.cookie.lastCall.args;
+               assert.strictEqual( call[ 1 ], 'undefined', 'undefined is 
value' );
+
+               mw.cookie.set( 'foo', false );
+               call = $.cookie.lastCall.args;
+               assert.strictEqual( call[ 1 ], 'false', 'false is a value' );
+
+               mw.cookie.set( 'foo', 0 );
+               call = $.cookie.lastCall.args;
+               assert.strictEqual( call[ 1 ], '0', '0 is value' );
+       } );
+
+       QUnit.test( 'set( key, value, expires )', 5, function ( assert ) {
+               var date, options;
+
+               date = new Date();
+               date.setTime( 1234 );
+
+               mw.cookie.set( 'foo', 'bar' );
+               options = $.cookie.lastCall.args[ 2 ];
+               assert.deepEqual( options.expires, expiryDate, 'Default cookie 
expiration is used' );
+
+               mw.cookie.set( 'foo', 'bar', date );
+               options = $.cookie.lastCall.args[ 2 ];
+               assert.strictEqual( options.expires, date, 'Custom expiration 
date' );
+
+               mw.cookie.set( 'foo', 'bar', null );
+               options = $.cookie.lastCall.args[ 2 ];
+               assert.strictEqual( options.expires, undefined, 'Expiry null 
forces session cookie' );
+
+               // Per DefaultSettings.php, when wgCookieExpiration is 0, the 
default should
+               // be session cookies
+               mw.config.set( 'wgCookieExpiration', 0 );
+
+               mw.cookie.set( 'foo', 'bar' );
+               options = $.cookie.lastCall.args[ 2 ];
+               assert.strictEqual( options.expires, undefined, 
'wgCookieExpiration=0 results in session cookies by default' );
+
+               mw.cookie.set( 'foo', 'bar', date );
+               options = $.cookie.lastCall.args[ 2 ];
+               assert.strictEqual( options.expires, date, 'Custom expiration 
when default is session cookies' );
+       } );
+
+       QUnit.test( 'set( key, value, options )', 4, function ( assert ) {
+               var date, call;
+
+               mw.cookie.set( 'foo', 'bar', {
+                       prefix: 'myPrefix',
+                       domain: 'myDomain',
+                       path: 'myPath',
+                       secure: true
+               } );
+
+               call = $.cookie.lastCall.args;
+               assert.strictEqual( call[0], 'myPrefixfoo' );
+               assert.deepEqual( call[ 2 ], {
+                       expires: expiryDate,
+                       domain: 'myDomain',
+                       path: 'myPath',
+                       secure: true
+               }, 'Options (without expires)' );
+
+               date = new Date();
+               date.setTime( 1234 );
+
+               mw.cookie.set( 'foo', 'bar', {
+                       expires: date,
+                       prefix: 'myPrefix',
+                       domain: 'myDomain',
+                       path: 'myPath',
+                       secure: true
+               } );
+
+               call = $.cookie.lastCall.args;
+               assert.strictEqual( call[0], 'myPrefixfoo' );
+               assert.deepEqual( call[ 2 ], {
+                       expires: date,
+                       domain: 'myDomain',
+                       path: 'myPath',
+                       secure: true
+               }, 'Options (incl. expires)' );
+       } );
+
+       QUnit.test( 'get( key ) - no values', 6, function ( assert ) {
+               var key, value;
+
+               mw.cookie.get( 'foo' );
+
+               key = $.cookie.lastCall.args[ 0 ];
+               assert.strictEqual( key, 'mywikifoo', 'Default prefix' );
+
+               mw.cookie.get( 'foo', undefined );
+               key = $.cookie.lastCall.args[ 0 ];
+               assert.strictEqual( key, 'mywikifoo', 'Use default prefix for 
undefined' );
+
+               mw.cookie.get( 'foo', null );
+               key = $.cookie.lastCall.args[ 0 ];
+               assert.strictEqual( key, 'mywikifoo', 'Use default prefix for 
null' );
+
+               mw.cookie.get( 'foo', '' );
+               key = $.cookie.lastCall.args[ 0 ];
+               assert.strictEqual( key, 'foo', 'Don\'t use default prefix for 
empty string' );
+
+               value = mw.cookie.get( 'foo' );
+               assert.strictEqual( value, null, 'Return null by default' );
+
+               value = mw.cookie.get( 'foo', null, 'bar' );
+               assert.strictEqual( value, 'bar', 'Custom default value' );
+       } );
+
+       QUnit.test( 'get( key ) - with value', 1, function ( assert ) {
+               var value;
+
+               $.cookie.returns( 'bar' );
+
+               value = mw.cookie.get( 'foo' );
+               assert.strictEqual( value, 'bar', 'Return value of cookie' );
+       } );
+
+       QUnit.test( 'get( key, prefix )', 1, function ( assert ) {
+               var key;
+
+               mw.cookie.get( 'foo', 'bar' );
+
+               key = $.cookie.lastCall.args[ 0 ];
+               assert.strictEqual( key, 'barfoo' );
+       } );
+
+} ( mediaWiki, jQuery ) );

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I217ef258aecf1acd335e2cea56ae08b22541c7d4
Gerrit-PatchSet: 24
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Phuedx <g...@samsmith.io>
Gerrit-Reviewer: Anomie <bjor...@wikimedia.org>
Gerrit-Reviewer: Bartosz DziewoƄski <matma....@gmail.com>
Gerrit-Reviewer: Catrope <roan.katt...@gmail.com>
Gerrit-Reviewer: Daniel Friesen <dan...@nadir-seen-fire.com>
Gerrit-Reviewer: JGonera <jgon...@wikimedia.org>
Gerrit-Reviewer: Jack Phoenix <j...@countervandalism.net>
Gerrit-Reviewer: Jdlrobson <jrob...@wikimedia.org>
Gerrit-Reviewer: Kaldari <rkald...@wikimedia.org>
Gerrit-Reviewer: Krinkle <krinklem...@gmail.com>
Gerrit-Reviewer: Mattflaschen <mflasc...@wikimedia.org>
Gerrit-Reviewer: Ori.livneh <o...@wikimedia.org>
Gerrit-Reviewer: Phuedx <g...@samsmith.io>
Gerrit-Reviewer: PleaseStand <pleasest...@live.com>
Gerrit-Reviewer: Robmoen <rm...@wikimedia.org>
Gerrit-Reviewer: Spage <sp...@wikimedia.org>
Gerrit-Reviewer: Swalling <swall...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to