Fandom Developers Wiki
mNo edit summary
m (defense against window.localstorage)
Line 59: Line 59:
 
// in localStorage with a timeout. (Timeout is in case someone alters the server
 
// in localStorage with a timeout. (Timeout is in case someone alters the server
 
// message which is unlikely but possible)
 
// message which is unlikely but possible)
var cache = window.localStorage && JSON.parse(window.localStorage.getItem(this._storageKey));
+
var cache = {};
  +
try {
  +
cache = window.localStorage && JSON.parse(window.localStorage.getItem(this._storageKey));
  +
} catch (ex) {
  +
logger.wrn('ls error', ex);
  +
cache = null;
  +
}
 
if (cache && cache.expiry > Date.now() && this._isSameConfig(cache, set)) {
 
if (cache && cache.expiry > Date.now() && this._isSameConfig(cache, set)) {
 
// Cache exists so we'll just use that
 
// Cache exists so we'll just use that
Line 98: Line 104:
 
tags[group[1]] = { u: $node.html(json[i]['*']).text(), weight: 1e100, order: 0 };
 
tags[group[1]] = { u: $node.html(json[i]['*']).text(), weight: 1e100, order: 0 };
 
}
 
}
if (!fromCache && $.storage) {
+
try {
window.localStorage.setItem(this._storageKey, JSON.stringify({
+
if (!fromCache && window.localStorage) {
expiry: Date.now() + 432e5, // +12hr from now
+
window.localStorage.setItem(this._storageKey, JSON.stringify({
data: json
+
expiry: Date.now() + 432e5, // +12hr from now
}));
+
data: json
  +
}));
  +
}
  +
} catch (ex) {
  +
logger.wrn('ls error', ex);
 
}
 
}
 
return tags;
 
return tags;

Revision as of 14:01, 26 May 2020

/*jshint curly:false smarttabs:true */
/*global UserTagsJS */

//
// MODULE: MediaWiki Groups
// Creates tags based on selected MediaWiki user groups.
// This module fetches its tag data from the server as well.
// NOTE: This module is complicated by Wikia's weird server setup that doesn't respond to queries
//    coherently.
// NOTE: We try to speed this up slightly by caching the tag data in localStorage
// NOTE: As far as examples of modules go, this and newuser are the most complicated, try reading
//    the others first.
// TODO: Cache needs to consider possibility that lang changes.
//
UserTagsJS.extensions.mwGroups = (function($, document) {
    "use strict";
    return {
        _storageKey: 'UserTags-mwGroups-TagDataCache',
        _groupRegex: /^group-(.+)-member$/,
        start: function(config, username, logger/*, lang*/) {
            // NOTE: bannedfromchat displays in Oasis but is not a user-identity group
            var groups = ['bannedfromchat', 'blocked', 'bot', 'bureaucrat', 'chatmoderator', 'checkuser', 'content-moderator', 'council', 'helper', 'rollback', 'staff', 'sysop', 'threadmoderator', 'vanguard', 'vstf', 'global-discussions-moderator', 'content-volunteer'];
            if (!$.isArray(config)) {
                config = groups;
            } else if (window.UserTagsMergeMWGroups === true) {
                config = _.union(config, groups);
            }
            // Convert array into a hash
            var set = {}, sanity = /^[A-Za-z0-9\-]+$/, messages = [];
            for (var i = 0 , len = config.length ; i < len ; ++i) {
                if (set[config[i]] === 1) { // Duplicates
                    logger.wrn('Duplicate group:', config[i]);
                    continue;
                } else if (sanity.test(config[i])) {
                    messages[messages.length] = 'group-' + config[i] + '-member';
                } else { // "group-all"/* is ignored since it's pointless
                    logger.wrn('Bad MediaWiki group:', config[i]);
                    continue;
                }
                set[config[i]] = 1;
            }
            this._groups = set;
            this._user = username;

            var data = {
                ajax: [
                {
                    type: 'user',
                    prop: ['groups']
                }, {
                    list: 'users',
                    ususers: username,
                    usprop: 'groups'
                }
                ]
            };

            // Rather than repeatedly query the same data over and over, we cache the tag data
            // in localStorage with a timeout. (Timeout is in case someone alters the server
            // message which is unlikely but possible)
            var cache = {};
            try {
                cache = window.localStorage && JSON.parse(window.localStorage.getItem(this._storageKey));
            } catch (ex) {
                logger.wrn('ls error', ex);
                cache = null;
            }
            if (cache && cache.expiry > Date.now() && this._isSameConfig(cache, set)) {
                // Cache exists so we'll just use that
                data.tags = this._processMessages(cache.data, true);
            } else {
                // Cache is either missing or expired so query for it
                data.ajax[2] = {
                    type: 'usermessage',
                    messages: messages
                };
            }

            return data;
        },
        // Check if the config has had new groups added to the list array which aren't in the
        // cache (which makes the cache worthless).
        _isSameConfig: function(cache, groups) {
            if (!$.isArray(cache = cache.data)) return false;
            var regex = this._groupRegex, data, i = 0, len = cache.length;
            groups = $.extend({}, groups);
            for ( ; i < len ; ++i) {
                if ((data = regex.exec(cache[i].name))) {
                    delete groups[data[1]];
                }
            }
            return $.isEmptyObject(groups);
        },
        _processMessages: function(json, fromCache) {
            var i = json.length, tags = {}, regex = this._groupRegex,
                $node = $(document.createElement('div')), group;
            while (i--) {
                if (json[i].missing !== void 0) continue;
                group = regex.exec(json[i].name);
                if (!group) continue;
                // Weight is boosted since we went to all the trouble of fetching it
                // from the server, only user should override this.
                // May contain HTML, discard that stuff
                tags[group[1]] = { u: $node.html(json[i]['*']).text(), weight: 1e100, order: 0 };
            }
            try {
                if (!fromCache && window.localStorage) {
                    window.localStorage.setItem(this._storageKey, JSON.stringify({
                        expiry: Date.now() + 432e5, // +12hr from now
                        data: json
                    }));
                }
            } catch (ex) {
                logger.wrn('ls error', ex);
            }
            return tags;
        },
        _processGroups: function(groups, data) {
            // Even if we ask for it, it may be missing anyway (e.g. global account with no edits)
            // Name may not match in list=users (shouldn't happen but it is an imprecise query)
            if (data.name === this._user && $.isArray(data = data.groups)) {
                for (var i = 0, len = data.length ; i < len ; ++i) {
                    if (this._groups[data[i]] === 1) {
                        groups[groups.length] = data[i];
                    }
                }
            }
            return groups;
        },
        generate: function(json, json2, json3) {
            // prepare to patch global blacklist for any requested group
            // export users groups here
            this._userGroups = false;// do not try to trick this very smart script!
            try {
                this._userGroups = json.query.users[0].groups;
            } catch (exc) {}
            // end of previous block
            return {
                groups: this._processGroups(
                    this._processGroups([], json.query.users[0]),
                    json2.query.users[0]
                ),
                // If we used the cache then json3 will be undefined (since we didn't ask for it)
                tags: !!json3 && this._processMessages(json3.query.allmessages)
            };
        }
    };
})(jQuery, document);