Merge "Improve test coverage for ApiStashEdit"
[lhc/web/wiklou.git] / resources / src / mediawiki.user.js
1 /**
2 * @class mw.user
3 * @singleton
4 */
5 /* global Uint32Array */
6 ( function ( mw, $ ) {
7 var userInfoPromise, pageviewRandomId;
8
9 /**
10 * Get the current user's groups or rights
11 *
12 * @private
13 * @return {jQuery.Promise}
14 */
15 function getUserInfo() {
16 if ( !userInfoPromise ) {
17 userInfoPromise = new mw.Api().getUserInfo();
18 }
19 return userInfoPromise;
20 }
21
22 // mw.user with the properties options and tokens gets defined in mediawiki.js.
23 $.extend( mw.user, {
24
25 /**
26 * Generate a random user session ID.
27 *
28 * This information would potentially be stored in a cookie to identify a user during a
29 * session or series of sessions. Its uniqueness should not be depended on unless the
30 * browser supports the crypto API.
31 *
32 * Known problems with Math.random():
33 * Using the Math.random function we have seen sets
34 * with 1% of non uniques among 200,000 values with Safari providing most of these.
35 * Given the prevalence of Safari in mobile the percentage of duplicates in
36 * mobile usages of this code is probably higher.
37 *
38 * Rationale:
39 * We need about 64 bits to make sure that probability of collision
40 * on 500 million (5*10^8) is <= 1%
41 * See https://en.wikipedia.org/wiki/Birthday_problem#Probability_table
42 *
43 * @return {string} 64 bit integer in hex format, padded
44 */
45 generateRandomSessionId: function () {
46 var rnds, i,
47 hexRnds = new Array( 2 ),
48 // Support: IE 11
49 crypto = window.crypto || window.msCrypto;
50
51 if ( crypto && crypto.getRandomValues && typeof Uint32Array === 'function' ) {
52 // Fill an array with 2 random values, each of which is 32 bits.
53 // Note that Uint32Array is array-like but does not implement Array.
54 rnds = new Uint32Array( 2 );
55 crypto.getRandomValues( rnds );
56 } else {
57 rnds = [
58 Math.floor( Math.random() * 0x100000000 ),
59 Math.floor( Math.random() * 0x100000000 )
60 ];
61 }
62 // Convert number to a string with 16 hex characters
63 for ( i = 0; i < 2; i++ ) {
64 // Add 0x100000000 before converting to hex and strip the extra character
65 // after converting to keep the leading zeros.
66 hexRnds[ i ] = ( rnds[ i ] + 0x100000000 ).toString( 16 ).slice( 1 );
67 }
68
69 // Concatenation of two random integers with entropy n and m
70 // returns a string with entropy n+m if those strings are independent
71 return hexRnds.join( '' );
72 },
73
74 /**
75 * A sticky generateRandomSessionId for the current JS execution context,
76 * cached within this class (also known as a page view token).
77 *
78 * @since 1.32
79 * @return {string} 64 bit integer in hex format, padded
80 */
81 getPageviewToken: function () {
82 if ( !pageviewRandomId ) {
83 pageviewRandomId = mw.user.generateRandomSessionId();
84 }
85
86 return pageviewRandomId;
87 },
88
89 /**
90 * Get the current user's database id
91 *
92 * Not to be confused with #id.
93 *
94 * @return {number} Current user's id, or 0 if user is anonymous
95 */
96 getId: function () {
97 return mw.config.get( 'wgUserId' ) || 0;
98 },
99
100 /**
101 * Get the current user's name
102 *
103 * @return {string|null} User name string or null if user is anonymous
104 */
105 getName: function () {
106 return mw.config.get( 'wgUserName' );
107 },
108
109 /**
110 * Get date user registered, if available
111 *
112 * @return {boolean|null|Date} False for anonymous users, null if data is
113 * unavailable, or Date for when the user registered.
114 */
115 getRegistration: function () {
116 var registration;
117 if ( mw.user.isAnon() ) {
118 return false;
119 }
120 registration = mw.config.get( 'wgUserRegistration' );
121 // Registration may be unavailable if the user signed up before MediaWiki
122 // began tracking this.
123 return !registration ? null : new Date( registration );
124 },
125
126 /**
127 * Whether the current user is anonymous
128 *
129 * @return {boolean}
130 */
131 isAnon: function () {
132 return mw.user.getName() === null;
133 },
134
135 /**
136 * Get an automatically generated random ID (persisted in sessionStorage)
137 *
138 * This ID is ephemeral for everyone, staying in their browser only until they
139 * close their browsing session.
140 *
141 * @return {string} Random session ID
142 */
143 sessionId: function () {
144 var sessionId = mw.storage.session.get( 'mwuser-sessionId' );
145 if ( !sessionId ) {
146 sessionId = mw.user.generateRandomSessionId();
147 mw.storage.session.set( 'mwuser-sessionId', sessionId );
148 }
149 return sessionId;
150 },
151
152 /**
153 * Get the current user's name or the session ID
154 *
155 * Not to be confused with #getId.
156 *
157 * @return {string} User name or random session ID
158 */
159 id: function () {
160 return mw.user.getName() || mw.user.sessionId();
161 },
162
163 /**
164 * Get the current user's groups
165 *
166 * @param {Function} [callback]
167 * @return {jQuery.Promise}
168 */
169 getGroups: function ( callback ) {
170 var userGroups = mw.config.get( 'wgUserGroups', [] );
171
172 // Uses promise for backwards compatibility
173 return $.Deferred().resolve( userGroups ).done( callback );
174 },
175
176 /**
177 * Get the current user's rights
178 *
179 * @param {Function} [callback]
180 * @return {jQuery.Promise}
181 */
182 getRights: function ( callback ) {
183 return getUserInfo().then(
184 function ( userInfo ) { return userInfo.rights; },
185 function () { return []; }
186 ).done( callback );
187 }
188 } );
189
190 /**
191 * @method stickyRandomId
192 * @deprecated since 1.32 use getPageviewToken instead
193 */
194 mw.log.deprecate( mw.user, 'stickyRandomId', mw.user.getPageviewToken, 'Please use getPageviewToken instead' );
195
196 }( mediaWiki, jQuery ) );