User:Murph9000/pagetriagestats-topreviewers.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:Murph9000/pagetriagestats-topreviewers. |
/**
* Page triage stats, top reviewers
* from API action=pagetriagestats
*
* Load from your common.js, for example:
*
* if ( mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Blankpage' ) {
* mw.loader.load( '/w/index.php?title=User:Murph9000/pagetriagestats-topreviewers.js&action=raw&ctype=text/javascript' );
* }
*
* Once added to your common.js, view the dynamically generated special page at:
*
* https://en.wikipedia.org/wiki/Special:BlankPage?action=pagetriagestats&topreviewers=last-day
* https://en.wikipedia.org/wiki/Special:BlankPage?action=pagetriagestats&topreviewers=last-week
* https://en.wikipedia.org/wiki/Special:BlankPage?action=pagetriagestats&topreviewers=last-month
*
* Copyright © 2017 User:Murph9000 @ English Wikipedia. All rights reserved.
*
* Released under the Creative Commons Attribution-ShareAlike 3.0 Unported License
* Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License
*
* Released under the Creative Commons Attribution-ShareAlike 4.0 International License
* Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_4.0_International_License
*
* Released under the GNU Free Documentation License
* Wikipedia:Text_of_the_GNU_Free_Documentation_License
*
* Released under the GNU General Public License, version 2 or later
* https://www.gnu.org/licenses/gpl-2.0.html
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
( function ( mw, $ ) {
'use strict';
var FILE = 'User:Murph9000/pagetriagestats-topreviewers.js';
console.info( FILE, 'startup' );
var $content, $spinner, topreviewersParam,
Api,
Html = mw.html,
conf = mw.config.get( [
'wgAction',
'wgCanonicalSpecialPageName',
'wgNamespaceIds',
'wgPageName',
] ),
messages = {
'pagetriagestats-filteredarticle': 'Filtered articles',
'pagetriagestats-filteredarticle-count': 'Count: $1',
'pagetriagestats-reviewedarticle': 'Reviewed articles',
'pagetriagestats-reviewedarticle-count': 'Count: $1',
'pagetriagestats-title': 'Page triage stats',
'pagetriagestats-topreviewers': 'Top reviewers',
'pagetriagestats-topreviewers-caption': 'Top reviewers, $1',
'pagetriagestats-topreviewers-last-day': 'last day',
'pagetriagestats-topreviewers-last-month': 'last month',
'pagetriagestats-topreviewers-last-week': 'last week',
'pagetriagestats-topreviewers-num': 'Number',
'pagetriagestats-topreviewers-total': 'Total',
'pagetriagestats-topreviewers-user': 'User',
'pagetriagestats-unreviewedarticle': 'Unreviewed articles',
'pagetriagestats-unreviewedarticle-count': 'Count: $1',
'pagetriagestats-unreviewedarticle-oldest': 'Oldest: $1',
},
topreviewersValues = [ 'last-day', 'last-week', 'last-month' ],
NS_USER = conf.wgNamespaceIds.user,
NS_USER_TALK = conf.wgNamespaceIds.user_talk,
contributionsTitle = 'Special:Contributions';
/**
* Make user link (or user contributions for unregistered users)
*
* mediawiki-1.28.2/includes/Linker.php
*
* @param int $userId User id in database.
* @param string $userName User name in database.
* @param string $altUserName Text to display instead of the user name (optional)
* @return string HTML fragment
*/
function userLink( userId, userName, altUserName ) {
var page,
classes = 'mw-userlink';
if ( userId === 0 ) {
page = mw.Title.newFromText( contributionsTitle + '/' + userName );
// PHP does a $altUserName = IP::prettifyIP( $userName );
classes += ' mw-anonuserlink'; // Separate link class for anons (bug 43179)
} else {
page = mw.Title.makeTitle( NS_USER, userName );
}
// Wrap the output with <bdi> tags for directionality isolation
return Html.element( 'a', {
href: page.getUrl(),
class: classes,
title: page.getPrefixedText()
}, new Html.Raw(
'<bdi>' +
Html.escape( altUserName !== undefined ? altUserName : userName ) +
'</bdi>'
) );
}
function userToolLinks( userId, userText ) {
var items, page;
items = [];
items.push( userTalkLink( userId, userText ) );
if ( userId ) {
page = mw.Title.newFromText( contributionsTitle + '/' + userText );
items.push( Html.element( 'a', {
href: page.getUrl(),
class: 'mw-usertoollinks-contribs',
title: page.getPrefixedText()
}, mw.msg( 'contribslink' ) )
);
}
return mw.msg( 'word-separator' )
+ '<span class="mw-usertoollinks">'
+ mw.message( 'parentheses',
items.join( mw.msg( 'pipe-separator' ) )
).text()
+ '</span>';
}
/**
* @param int $userId User id in database.
* @param string $userText User name in database.
* @return string HTML fragment with user talk link
*/
function userTalkLink( userId, userText ) {
var page = mw.Title.makeTitle( NS_USER_TALK, userText ),
classes = 'mw-usertoollinks-talk';
return Html.element( 'a', {
href: page.getUrl(),
class: classes,
title: page.getPrefixedText()
}, mw.msg( 'talkpagelinktext' ) );
}
function pagetriagestatsCallback( data ) {
console.log( FILE, 'pagetriagestatsCallback', data );
var stats, topreviewers, rec, total, $div, $table, $tbody;
if ( data.pagetriagestats.result !== 'success' ) {
mw.log.error( FILE, 'API action=pagetriagestats did not return success' );
$content.append(
'<p><big style="color:red">API did not return success</big></p>'
);
return;
}
$div = $( '<div>' ).addClass( 'pagetriagestats' );
$content.append( $div );
stats = data.pagetriagestats.stats;
$div.append(
Html.element( 'dl', { class: 'stats' }, new Html.Raw(
Html.element( 'dt', { class: 'unreviewedarticle' },
mw.msg( 'pagetriagestats-unreviewedarticle' ) ) +
Html.element( 'dd', { class: 'unreviewedarticle-count' },
mw.msg( 'pagetriagestats-unreviewedarticle-count',
stats.unreviewedarticle.count ) ) +
Html.element( 'dd', { class: 'unreviewedarticle-oldest' },
mw.msg( 'pagetriagestats-unreviewedarticle-oldest',
new Date( stats.unreviewedarticle.oldest.replace(
/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/,
'$1-$2-$3T$4:$5:$6Z'
) )
)
) +
Html.element( 'dt', { class: 'reviewedarticle' },
mw.msg( 'pagetriagestats-reviewedarticle' ) ) +
Html.element( 'dd', { class: 'reviewedarticle-count' },
mw.msg( 'pagetriagestats-reviewedarticle-count',
stats.reviewedarticle.reviewed_count ) ) +
Html.element( 'dt', { class: 'filteredarticle' },
mw.msg( 'pagetriagestats-filteredarticle' ) ) +
Html.element( 'dd', { class: 'filteredarticle-count' },
mw.msg( 'pagetriagestats-filteredarticle-count',
stats.filteredarticle ) )
) )
);
topreviewers = stats.topreviewers;
$div.append(
Html.element( 'h2', {}, mw.msg( 'pagetriagestats-topreviewers' ) )
);
$table = $( '<table>' ).addClass( 'topreviewers wikitable' );
$table.append( Html.element( 'caption', {},
mw.msg( 'pagetriagestats-topreviewers-caption',
mw.msg( 'pagetriagestats-topreviewers-' + topreviewersParam ) )
) );
$table.append( Html.element( 'thead', {}, new Html.Raw(
Html.element( 'tr', {}, new Html.Raw(
Html.element( 'th',
{ scope: 'col' },
'#' ) +
Html.element( 'th',
{ scope: 'col', style: 'text-align: left;' },
mw.msg( 'pagetriagestats-topreviewers-user' ) ) +
Html.element( 'th',
{ scope: 'col', style: 'text-align: left;' },
mw.msg( 'pagetriagestats-topreviewers-num' ) )
) )
) ) );
$tbody = $( '<tbody>' );
total = 0;
for ( var i in topreviewers ) {
rec = topreviewers[ i ];
$tbody.append( Html.element( 'tr', {}, new Html.Raw(
Html.element( 'th', { scope: 'row' }, i ) +
Html.element( 'td', {}, new Html.Raw(
userLink( rec.user_id, rec.user_name ) +
userToolLinks( rec.user_id, rec.user_name )
) ) +
Html.element( 'td', {}, rec.num )
) ) );
total += Number(rec.num);
}
$table.append( $tbody );
$table.append( Html.element( 'tfoot', {}, new Html.Raw(
Html.element( 'tr', {}, new Html.Raw(
//Html.element( 'th' ) +
Html.element( 'th',
{ scope: 'row', style: 'text-align: left;', colspan: 2 },
mw.msg( 'pagetriagestats-topreviewers-total' ) ) +
Html.element( 'th',
{ scope: 'col', style: 'text-align: left;' },
total )
) )
) ) );
$div.append( $table );
}
function initPage() {
console.log( FILE, 'ready' );
var title = mw.msg( 'pagetriagestats-title' );
$content = $( '#mw-content-text' );
if ( conf.wgCanonicalSpecialPageName === 'Blankpage' ) {
document.title = mw.msg( 'pagetitle', title );
// Normal skins use #firstHeading
// Mobile skin uses #section_0 for no good reason
$( '#firstHeading, #section_0' ).text( title );
$content.empty();
} else {
$content.append( Html.element( 'h1', {}, title ) );
}
$spinner = $.createSpinner( { size: 'large', type: 'block' } );
$content.append( $spinner );
}
function loaderCallback() {
console.log( FILE, 'loader done' );
var maxage;
if ( conf.wgCanonicalSpecialPageName === 'Blankpage' &&
mw.util.getParamValue( 'action' ) !== 'pagetriagestats' ) {
console.info( FILE + ': rejecting, not a request for this script' );
return $.Deferred().reject();
}
mw.messages.set( messages );
topreviewersParam = mw.util.getParamValue( 'topreviewers' );
if ( !topreviewersParam ||
!topreviewersValues.includes( topreviewersParam ) ) {
topreviewersParam = 'last-week';
}
/**
* https://phabricator.wikimedia.org/diffusion/EPTR/browse/master/includes/PageTriageUtil.php
*
* Data has server-side caching, with expiry as follows:
* last-day: 60 * 60
* last-week: 24 * 60 * 60
* last-month: 24 * 60 * 60
*
* Set our maxage based on that (but a small fraction of it, to avoid
* doubling the delay).
*/
switch ( topreviewersParam ) {
case 'last-day':
maxage = 5 * 60;
break;
//case 'last-week':
//case 'last-month':
default:
maxage = 60 * 60;
}
Api = new mw.Api( {
parameters: {
formatversion: 2
}
} );
return $.when(
Api.get( {
action: 'pagetriagestats',
topreviewers: topreviewersParam,
smaxage: maxage,
maxage: maxage
} ),
$.when(
Api.loadMessagesIfMissing( [
'contribslink',
'pagetitle',
'parentheses',
'pipe-separator',
'talkpagelinktext',
'word-separator',
], {
smaxage: 86400,
maxage: 86400
} ),
$.ready
).then( initPage )
);
}
if ( conf.wgCanonicalSpecialPageName === 'Blankpage' ||
( conf.wgPageName === FILE && conf.wgAction === 'submit' ) ) {
mw.loader.using( [
'mediawiki.Title',
'mediawiki.api',
'mediawiki.api.messages',
'mediawiki.jqueryMsg',
'mediawiki.util',
'jquery.spinner'
] ).then( loaderCallback ).done( function ( data ) {
pagetriagestatsCallback.apply( this, data );
mw.hook( 'wikipage.content' ).fire( $content );
} ).always( function () {
if ( $spinner ) {
$spinner.remove();
}
} );
}
} )( mediaWiki, jQuery );