MediaWiki:Tool/RightsLog.js
Перейти к навигации
Перейти к поиску
Возможно, этот код документирован.
//"AJAX log" for [[special:log/rights]]; called from [[MediaWiki:Log.js]]
mw.loader.using('mediawiki.util').done(function () {
//on script load
if( mw.config.get('wgCanonicalSpecialPageName') == 'Log' && /&type=rights|\/rights/.test(document.URL) ){
mw.loader.load('jquery.spinner')
$(rightsLog)
}
});
function rightsLog(){
mw.util.addCSS('\
#log-form {font-size: 13px}\
td.timestamp, td.sysop, td.parsedcomment {font-size:smaller}\
td.timestamp {white-space:nowrap}\
span.button {cursor:pointer; color: #2F6FAB}\
.added {color:green}\
.removed {color:red}\
.removed.useless {font-size:smaller; color:gray; text-decoration:line-through}\
.same {font-size:smaller; color:gray}\
.same.useless {color:#a0a}\
')
var uselessFlags = {
autoeditor: ['editor', 'bot', 'sysop'],
uploader: ['filemover', 'closer', 'sysop'],
suppressredirect: ['filemover', 'closer', 'sysop'],
rollbacker: ['sysop'],
filemover: ['sysop'],
closer: ['sysop']
}
var flagName =
{uploader: 'загружающий'
,autoeditor: 'автопатрулируемый'
,editor: 'патрулирующий'
,rollbacker: 'откатывающий'
,closer: 'итогоподводящий'
,suppressredirect: 'переим-без-перенапр.'
,filemover: 'переим-файлы'
,sysop: 'администратор'
,bureaucrat: 'бюрократ'
}
var msg =
{legend:'Альтернативный журнал присвоения прав '
+ aLink('MediaWiki talk:Tool/RightsLog.js', '(документация)')
+ ' <span id=log-close class=button>[закрыть]</span>'
,count:'Показано записей: <span id=log-count></span>'
,next:'Добавить 50 следующих (200 | 500)'
}
var mm = function (txt){ return msg[txt] || txt }
var rcvCount
var api = apiList(
{ list: 'logevents',
leaction : 'rights/rights',
leprop: 'timestamp|title|user|details|parsedcomment'
},
receive
)
//hide old content
var frm = $('#mw-log-user').closest('form')
var oldContent = $( document.getElementById('mw-content-text') || frm.parent() ).hide()
//clone existing form
frm = frm.clone(true)
var newContent = $('<div />')
.insertAfter(oldContent)
.append( frm.attr('id','log-form'), '<div id=log-output />' )
//modify form
frm.find('select[name="type"]').remove()
frm.find('label[for="tagfilter"]').closest('p').remove()
frm.find('legend').html( mm('legend') )
$('#log-close').click(function(){ newContent.remove(); oldContent.show() })
frm.find('input[type="submit"]').click(clickSubmit)
//start
frm.find('input[type="submit"]').click()
return
function clickSubmit(e){
e.preventDefault()
//<input name=page>: prepend 'user:' if omitted
var p = inp('page')
if( p && ! /:/.test(p) ) inp('page', 'user:' + p)
//get params from the form
var params = {lelimit: mw.user.options.get('rclimit') || 20 }
p = inp('user'); if( p ) params.leuser = p
p = inp('page'); if( p ) params.letitle = p
//and timestamp
var ts, mo = inp('month'), yr = inp('year')
if( mo > 0 ) ts = ( yr || new Date().getUTCFullYear().toString() ) + pad0(mo) // !!!
else if( yr ) ts = yr + '01'
if( ts ) params.lestart = ts + '00000000'
//start
rcvCount = 0
$('#log-output').empty()
api.getFirst(params)
function inp(nm, vv){ //set/get input value
var el = frm.find('input[name="'+nm+'"]')
if( vv ) el.val(vv)
else return $.trim( el.val() )
}
}
function receive(events) {
if ( ! $('#log-table').length ){ //create table header
$('#log-output').append(
'<table class=wikitableX id=log-table cellspacing=10><tr>'
+ aTH('Time', '2/26/Clock_simple.svg')
+ ( api.params.letitle ? '' :
aTH('User', '1/12/User_icon_2.svg') )
+ aTH('Rights', '2/2d/Flag_green.png')
+ ( api.params.leuser ? '' :
aTH('Sysop') )
+ aTH('Comment')
+ '</tr></table>'
+ '<div>' + mm('count') + '</div>'
+ '<div id=log-next>' + mm('next').replace(/\d+/g, '<span class=more>$&</span>') + '</div>'
)
$('span.more')
.addClass('button')
.click( function(){ api.getNext( {lelimit: $(this).text()} ) })
}
var htm = '', rOld, rNew, right
$.each(events, function(i, ev){
//calc added/removed rights
rOld = ev.rights['old'].split(', ')
rNew = ev.rights['new'].split(', ')
right = {}
$.each( rOld, function(j, r){ right[r] = 'removed' })
$.each( rNew, function(j, r){ right[r] = right[r] ? 'same' : 'added' })
//output row
htm += '<tr>'
+ aTD( ev, 'timestamp' )
+ ( api.params.letitle ? '' :
aTD( ev.title.replace(/^[^:]+:/,''), 'user' ) )
+ aTD( listR('added') + listR('removed') + listR('same'), 'rights' )
+ ( api.params.leuser ? '' :
aTD( ev, 'user', 'sysop' ) )
+ aTD( ev, 'parsedcomment')
+ '</tr>'
})
$('#log-table').append('<tbody>'+htm+'</tbody>')
//update count and 'next' links
rcvCount += events.length
$('#log-count').text( rcvCount )
$('#log-next').toggle( !!api.cont )
function listR(what){
var s = ''
$.each( right, function(r, type){
if( type == what )
s += '<span class="' + type + isUseless(r) + '">' + ( flagName[r] || r ) + '</span> '
})
return s
}
function isUseless(r){ //using rNew[]
var uf = uselessFlags[r] || []
for( var i=0; i<uf.length; i++ )
if( $.inArray(uf[i], rNew) != -1 ) return ' useless'
return ''
}
}
}//rightsLog
// =================================================================================
function wait(isWait){ //spinner and hourglass cursor
document.body.style.cursor = isWait ? 'wait' : ''
if( ! $.createSpinner ) return //module not loaded yet
if( isWait ) $.createSpinner({ id:'rl', size:'large', type:'block' }).appendTo('#log-output')
else $.removeSpinner('rl')
}
//Usage:
// api = apiList ( defaultParams, callback ); api.getFirst ( [params] ) ; if( api.cont ) api.getNext ( [params] )
function apiList(defParams, callback){
var name = defParams.list
var api = new mw.Api( {parameters: defParams} )
var recv = function (resp, textStatus, jqXHR){
wait()
//updateServerTime(jqXHR)
api.cont = getChild(resp, 'query-continue.' + name) //save query-continue
callback( getChild(resp, 'query.' + name) ) //return only data
}
// api.cont = null; api.params = {}
api.getFirst = function(p){ wait(true); api.params = p; this.get( api.params).done(recv) }
api.getNext = function(p){ wait(true); this.get( $.extend({}, api.params, p, api.cont)).done(recv) }
return api
}
function getChild(obj, path){ //example: getChild( data , 'query.pages..somekey' )
path = path.split('.')
for( var i=0; i<path.length; i++ ){
var key = path[i]
if( key == '' ) for (key in obj) break //get any child
if( obj ) obj = obj[key]
}
return obj
}
function aTD(obj, key, clss){
return '<td class="' + (clss || key) + '">' + aVal( obj, key ) + '</td>'
}
function aTH(tip, ico, clss){
return '<th title="' + tip + '" class="' + (clss||tip).toLowerCase() + '" >'
+ (ico ? aIcon(ico, 15) : '') + '</th>'
}
function aVal (obj, key){
var val = typeof obj == 'object' ? obj[key] : obj
switch ( key ){
case 'title': case 'page': return aLink(val)
case 'user': return aLink('Special:Contributions/'+val, val)
case 'touched': case 'timestamp': return Ts2String(val)
//return inHours( wgServerTime - Ts2Date(val) )
case 'hours': return inHours(val)
//case 'size': case 'oldlen': case 'newlen':
default: return val
}
}
function aLink (page, name, attr){
name = name || page
if( name.length > 40) name = name.substr(0, 37) + '…'
attr = $.extend(
{ href: mw.util.getUrl(page),
title: page == name ? '' : page.replace(/"/g,'"')
},
attr
)
return mw.html.element( 'a', attr, name )
}
function aIcon(src, size, attr){ //returns <img ...> from Commons
if( size ) src = 'thumb/'+src+'/'+size+'px-'+src.split('/')[2] + ( /\.svg$/.test(src) ? '.png' : '' )
return '<img src="http://upload.wikimedia.org/wikipedia/commons/'
+ src + '" ' + (attr||'')+'>'
}
function pad0(v, len){ // 6 -> '06'
len = len || 2
v = v.toString()
while (v.length < len) v = '0'+v
return v
}
function Ts2String(ts){
var m = ts.replace(/\D/g,'').match(/(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/)
//var window.wmDateOutput = 'hh:mm, dd mon yy' // ?
return '<small>hh:mm,</small> dd mon yy'
.replace('yy', m[1])
.replace('mon', wgMonthNamesShort[parseInt(m[2],10)])
.replace('dd', m[3])
.replace('hh', m[4])
.replace('mm', m[5])
.replace('ss', m[6])
}
//20081226220605 or 2008-01-26T06:34:19Z -> date
function Ts2Date(ts){
var m = ts.replace(/\D/g,'').match(/(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/)
return new Date ( Date.UTC(m[1], m[2]-1, m[3], m[4], m[5], m[6]) )
}