MediaWiki:Gadget-MarkAdmins-updater.js
表示
お知らせ: 保存した後、ブラウザのキャッシュをクリアしてページを再読み込みする必要があります。
多くの Windows や Linux のブラウザ
- Ctrl を押しながら F5 を押す。
Mac における Safari
Mac における Chrome や Firefox
- ⌘ Cmd と ⇧ Shift を押しながら R を押す。
詳細についてはWikipedia:キャッシュを消すをご覧ください。
/** * Updates the JSON data for [[MediaWiki:Gadget-MarkAdmins.js]]. * @module * @link https://ja.wikipedia.org/wiki/MediaWiki:Gadget-MarkAdmins.js * @link https://ja.wikipedia.org/wiki/MediaWiki:Gadget-MarkAdmins-data.json * * Note that data updating should be done by sysops because of the current limitation of mediawiki action API. * list=allusers and list=globalallusers limit their results to 5000 entries for users with apihighlimits, and * 500 entries for those without it. The current version of this script doesn't handle API request continuation * because there is an issue with list=globalallusers that the response JSON doesn't have a "continue" property * (see T241940 on phabricator). */ //<nowiki> (function(mw,$){ // ************************************************************************************************************* // Is the current user a sysop? if(mw.config.get('wgUserGroups').indexOf('sysop')===-1)return; // Define user groups for which we collect usernames. // Note that the order of items affects the order of markers. varlocalGroups=[ 'sysop', 'suppress', 'checkuser', 'bureaucrat', 'interface-admin', 'abusefilter', 'accountcreator', 'bot', 'eliminator', 'rollbacker' ]; varglobalGroups=[ 'founder', 'steward', 'ombuds', 'staff', 'sysadmin', 'global-sysop', 'abusefilter-maintainer', 'abusefilter-helper', 'global-interface-editor', 'global-bot', 'global-deleter', 'global-rollbacker', 'vrt-permissions' ]; varmetaGroups=[ 'global-renamer', 'wmf-officeit', 'wmf-supportsafety' ]; varallGroups=localGroups.concat(globalGroups,metaGroups); // Load dependent modules varapi,fApi; $.when( mw.loader.using(['mediawiki.api','mediawiki.ForeignApi','mediawiki.util','mediawiki.notification']), $.ready ).then(function(){ api=newmw.Api(); fApi=newmw.ForeignApi('https://meta.wikimedia.org/w/api.php'); varportlet=mw.util.addPortletLink( document.getElementById('p-cactions')?'p-cactions':'p-personal', '#', 'MarkAdmins-updater', 'ca-mau' ); if(portlet){ portlet.addEventListener('click',updateJson); }else{ console.error('Failed to create a portlet link for MarkAdmins-updater.'); } }); // ********************************************************************************************************* /** * Main function to update the JSON. Trigerred when the portletlink is hit. * @param {Event} e */ functionupdateJson(e){ e.preventDefault(); varautoUpdate=confirm('MarkAdminsのJSONデータを取得します。更新が必要な際、自動編集を行う場合は "OK" を、行わない場合は "Cancel" を押してください。'); mw.notification.notify('JSONデータを取得しています...'); getUsersInGroups().then(function(groups){ if(groups){ console.log(groups); mw.notification.notify('JSONデータをコンソールに出力しました'); }else{ mw.notification.notify('JSONデータの取得に失敗しました'); return; } varpagetitle='MediaWiki:Gadget-MarkAdmins-data.json'; getLatestRevision(pagetitle).then(function(resObj){ if(!resObj)return; vargroupsOld=JSON.parse(resObj.content); if(isSameDataObject(groups,groupsOld)){ mw.notification.notify('データは最新のため更新不要です'); return; }elseif(!autoUpdate){ mw.notification.notify('データの更新が必要です'); return; }else{ mw.notification.notify('データを更新しています...'); } api.postWithEditToken({ action:'edit', title:pagetitle, text:JSON.stringify(groups,null,4), summary:'データの更新 ([[H:MA#UPDATER|MarkAdmins-updater]])', basetimestamp:resObj.basetimestamp, starttimestamp:resObj.curtimestamp, formatversion:'2' }).then(function(res){ if(res&&res.edit&&res.edit.result==='Success'){ mw.notification.notify('データを更新しました'); }else{ mw.notification.notify('データの更新に失敗しました (不明なエラー)'); } }).catch(function(code,err){ console.error(err); varmsg=err&&err.error&&err.error.info?' ('+err.error.info+')':''; mw.notification.notify('データの更新に失敗しました'+msg); }); }); }); } /** * Reduce an array of objects fetched from the API to an array of objects with specific properties * @param {Array<{id: number, name: string, groups: Array<string>}>} responseArray API response array in res.query[keyname] * @param {Array<string>} groupsArray * @returns {Array<UserGroup>} * @typedef UserGroup * @type {object} * @property {string} name * @property {Array<string>} groups The array is filtered */ functionreduceResponse(responseArray,groupsArray){ /** @type {Array<UserGroup>} */ vararr=[]; returnresponseArray.reduce(function(acc,obj){ acc.push({ name:obj.name, groups:obj.groups.filter(function(group){ returngroupsArray.indexOf(group)!==-1;// Only leave relevant group names }) }); returnacc; },arr); } /** * @param {"local"|"global"|"meta"} groupType * @returns {JQueryPromise<undefined|Array<UserGroup>>} */ functionqueryUserGroups(groupType){ vardef=$.Deferred(); varlistType,groupsArray,API; varparams={ action:'query', list:(listType=(groupType==='global'?'globalallusers':'allusers')), formatversion:'2' }; switch(groupType){ case'local': $.extend(params,{ aulimit:'max', augroup:(groupsArray=localGroups).join('|'), auprop:'groups' }); API=api; break; case'global': $.extend(params,{ agulimit:'max', agugroup:(groupsArray=globalGroups).join('|'), aguprop:'groups', }); API=api; break; case'meta': $.extend(params,{ aulimit:'max', augroup:(groupsArray=metaGroups).join('|'), auprop:'groups' }); API=fApi; break; default: returndef.resolve(); } API.get(params) .then(function(res){ varresArray; if(!res||!res.query||!(resArray=res.query[listType])||resArray.length===0){ def.resolve(); }else{ def.resolve(reduceResponse(resArray,groupsArray)); } }).catch(function(code,err){ console.error(err); def.resolve(); }); returndef.promise(); } /** * @returns {JQueryPromise<undefined|Object.<string, Array<string>>>} */ functiongetUsersInGroups(){ return$.when.apply($,[ queryUserGroups('local'), queryUserGroups('global'), queryUserGroups('meta') ]).then(function(){ varargs=arguments, resLocal=args[0], resGlobal=args[1], resMeta=args[2]; if(!resLocal||!resGlobal||!resMeta)return; varresAll=resLocal.concat(resGlobal,resMeta); returnresAll.reduce(function(acc,obj){ varusername,groupsArr; if((username=obj.name)&&(groupsArr=obj.groups)&&Array.isArray(obj.groups)){ groupsArr=groupsArr.sort(function(a,b){ returnallGroups.indexOf(a)-allGroups.indexOf(b); }); if(!acc[username]){ acc[username]=groupsArr; }else{ acc[username]=acc[username].concat(groupsArr); } } returnacc; },Object.create(null)); }); } /** * @param {string} pagetitle * @returns {JQueryPromise<undefined|{title: string, missing: boolean, revid: string, basetimestamp: string, curtimestamp: string, content: string}>} */ functiongetLatestRevision(pagetitle){ vardef=$.Deferred(); api.get({ action:'query', titles:pagetitle, prop:'revisions', rvprop:'timestamp|content|ids', rvslots:'main', curtimestamp:1, formatversion:'2' }).then(function(res){ varresPages; if(!res||!res.query||!(resPages=res.query.pages)||resPages.length===0){ returndef.resolve(); } resPages=resPages[0]; def.resolve({ title:resPages.title, missing:resPages.missing, revid:resPages.missing?undefined:resPages.revisions[0].revid.toString(), basetimestamp:resPages.missing?undefined:resPages.revisions[0].timestamp, curtimestamp:res.curtimestamp, content:resPages.missing?undefined:resPages.revisions[0].slots.main.content }); }).catch(function(code,err){ console.error(err); def.resolve(); }); returndef.promise(); } /** * @param {Array<(boolean|string|number|undefined|null)>} array1 * @param {Array<(boolean|string|number|undefined|null)>} array2 * @param {boolean} [orderInsensitive] If true, ignore the order of elements * @returns {boolean|null} Null if non-arrays are passed as arguments */ functionarraysEqual(array1,array2,orderInsensitive){ if(!Array.isArray(array1)||!Array.isArray(array2)){ returnnull; }elseif(orderInsensitive){ returnarray1.length===array2.length&&array1.every(function(el){ returnarray2.indexOf(el)!==-1; }); }else{ returnarray1.length===array2.length&&array1.every(function(el,i){ returnarray2[i]===el; }); } } /** * @param {object} dataObj1 * @param {object} dataObj2 * @returns {boolean} */ functionisSameDataObject(dataObj1,dataObj2){ if(!arraysEqual(Object.keys(dataObj1),Object.keys(dataObj2),true)){ returnfalse; }else{ returnObject.keys(dataObj1).every(function(key){ varpropArr1=dataObj1[key]; varpropArr2=dataObj2[key]; returnarraysEqual(propArr1,propArr2); }); } } // ************************************************************************************************************* // @ts-ignore "Cannot find name 'mediaWiki'." })(mediaWiki,jQuery); //</nowiki>