MediaWiki:Notifier.js
Jump to navigation
Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
Documentation for this user script can be added at MediaWiki:Notifier. |
- Report page listing warnings and errors.
// <source lang="javascript">
/*
Implementation for quick-deletion user notification links.
Author: [[User:Lupo]], January 2009
License: Quadruple licensed GFDL, GPL, LGPL and Creative Commons Attribution 3.0 (CC-BY-3.0)
Choose whichever license of these you like best :-)
*/
if (typeof (Notifier) == 'undefined') { // Guard against double inclusions
importScript ('MediaWiki:Utilities.js');
importScript ('MediaWiki:QuickModify.js');
importScript ('MediaWiki:TextCleaner.js');
if (typeof (notification_definitions) != 'object')
var notification_definitions = {};
notification_definitions.copyvio =
{ img_template : '\{\{copyvio|1=$1\}\}'
,talk_template : '\{\{subst:copyvionote|1=$F\}\}\~\~\~\~'
,img_summary : 'Marking as possible copyright violation'
,talk_summary : 'Notification of possible copyright violation'
,prompt_text : 'Why is this file a copyright violation?\n'
+ 'Give a reason and/or external source (URL or such).'
};
notification_definitions.nosource =
{ img_template : '\{\{no source since|month=\{\{subst:CURRENTMONTHNAME\}\}|day=\{\{subst:CURRENTDAY\}\}|year=\{\{subst:CURRENTYEAR\}\}\}\}'
,talk_template : '\{\{subst:image source|1=$F\}\}\~\~\~\~'
,img_summary : 'File has no source'
,talk_summary : 'Where does [[:$F]] come from?'
};
notification_definitions.nolicense =
{ img_template : '\{\{no license since|month=\{\{subst:CURRENTMONTHNAME\}\}|day=\{\{subst:CURRENTDAY\}\}|year=\{\{subst:CURRENTYEAR\}\}\}\}'
,talk_template : '\{\{subst:image license|1=$F\}\}\~\~\~\~'
,img_summary : 'Missing license'
,talk_summary : 'File [[:$F]] does not have a license'
};
notification_definitions.nopermission =
{ img_template : '\{\{no permission since|month=\{\{subst:CURRENTMONTHNAME\}\}|day=\{\{subst:CURRENTDAY\}\}|year=\{\{subst:CURRENTYEAR\}\}\}\}'
,talk_template : '\{\{subst:image permission|1=$F\}\}\~\~\~\~'
,img_summary : 'Missing permission'
,talk_summary : 'Please send permission for [[:$F]] to [[COM:OTRS|OTRS]]'
};
var nfd_delReq = "Commons:Deletion requests";
var nfd_deleteTemplate = "delete";
var nfd_idwTemplate = "subst:idw";
if (typeof (notifier_timeout) == 'undefined')
var notifier_timeout = 1500; // 1.5 seconds
var notifier_user_query = "Which user do you want to notify?";
var Notifier =
{
/* $1 = input from user (prompt)
$F = title (which should include the namespace)
$U = user name, if given or found
img_template is mandatory.
if not talk_template exists, user will not be notified, even if given.
if talk_template exists, but no user is given, and we're on a page in the File namespace, try to
get uploader's name from the topmost entry in the file history on the page.
if $1 is used, optional prompt_text may be specified. Default if none given is "Reason:"
*/
mark : function (what, title, user, revision, dont_save, dont_close)
{
if (!what) return;
var same_window = false;
var is_edit_page = false;
if (!title) {
title = mw.config.get('wgPageName');
is_edit_page = (mw.config.get('wgAction') == 'edit' && document.getElementById ('editform') != null);
same_window = (mw.config.get('wgAction') == 'view' || is_edit_page);
}
if (!notification_definitions[what]) {
alert ('Unknown notification for "' + what + '"; aborted.');
return;
}
var img_template = notification_definitions[what].img_template;
if (!img_template) return;
var talk_template = notification_definitions[what].talk_template;
var img_summary = notification_definitions[what].img_summary || "";
var talk_summary = notification_definitions[what].talk_summary || "";
var reason = "";
if ((img_template + ' ' + talk_template + ' ' + img_summary + ' ' + talk_summary).indexOf ('$1') >= 0)
{
reason = Notifier.safe_prompt (notification_definitions[what].prompt_text || 'Reason:');
if (!reason || reason.length == 0) return; // Cancelled
}
if (talk_template) {
if (!user || user.length == 0) {
user = Notifier.get_user_from_page ();
if (!user) {
Notifier.get_user_from_server
( title
, function (user) {
Notifier.do_mark
( title, user, reason
, img_template, img_summary, talk_template, talk_summary, revision
, dont_save, dont_close, same_window, is_edit_page);
}
);
return;
}
}
} else
user = null;
Notifier.do_mark
( title, user, reason
, img_template, img_summary, talk_template, talk_summary, revision
, dont_save, dont_close, same_window, is_edit_page);
},
do_mark : function ( title, user, reason
, img_template, img_summary, talk_template, talk_summary, revision
, dont_save, dont_close, same_window, is_edit_page)
{
function subst (str)
{
return replaceVars (str, '$', '1FU', reason, title, user);
}
var user_action = "";
if (user) {
user_action =
QuickModify.actions (
['a', '\n' + subst (talk_template)]
, [(dont_save ? 'e' : (dont_close ? 's' : 'c')), subst (talk_summary)]
);
}
if (is_edit_page) {
var text = document.getElementById ('wpTextbox1');
text.value = subst (img_template) + '\n' + text.value;
scrollTextArea (text, true);
setEditSummary (subst (img_summary));
if (user) {
var edit_lk = mw.config.get('wgServer') + wgScript
+ '?title=User_talk:' + encodeURIComponent (user) + '&action=edit';
QuickModify.execute (
edit_lk
, user_action
);
}
} else {
var edit_lk = mw.config.get('wgServer') + wgScript
+ '?title=' + encodeURIComponent (title) + '&action=edit';
QuickModify.execute (
edit_lk
, QuickModify.actions (
(revision ? ['h', revision] : null)
, ['i', subst (img_template) + '\n']
, [(dont_save ? 'e' : (user && !dont_close && !same_window ? 'c' : 's')), subst (img_summary)]
)
+ (user ? QuickModify.join (
'User talk:' + user
, user_action
)
: ""
)
, same_window
);
} // end if
},
nominateForDeletion : function (title, user, revision, prompt_text, dont_save, dont_close)
{
var reason = Notifier.safe_prompt (prompt_text || 'Why should this file be deleted?', "");
if (!reason || reason.length == 0) return;
var same_window = false;
var is_edit_page = false;
if (!title) {
title = mw.config.get('wgPageName');
is_edit_page = (mw.config.get('wgAction') == 'edit' && document.getElementById ('editform') != null);
same_window = (mw.config.get('wgAction') == 'view' || is_edit_page);
}
if (!user || user.length == 0) {
user = Notifier.get_user_from_page ();
if (!user) {
Notifier.get_user_from_server
( title
, function (user) {
if (!user) return;
Notifier.do_delreq ( title, user, reason, revision
, dont_save, dont_close, same_window, is_edit_page);
}
);
return;
}
}
Notifier.do_delreq ( title, user, reason, revision
, dont_save, dont_close, same_window, is_edit_page);
},
do_delreq : function ( title, user, reason, revision
, dont_save, dont_close, same_window, is_edit_page)
{
var today = new Date ();
var day = today.getUTCDate ();
day = (day < 10 ? '0' : "") + day;
var month = today.getUTCMonth () + 1;
month = (month < 10 ? '0' : "") + month;
var del_req_log = nfd_delReq + '/' + today.getUTCFullYear () + '/' + month + '/' + day;
var del_req = nfd_delReq + '/' + title.replace(/\_/g, ' ');
// Create the DR, add it in the log, tag the page, and notify the user. Make sure that the page
// is tagged only once the DR exists, otherwise the delete-template may claim the nomination was
// incomplete and we would need to purge the page at the end.
var edit_lk = null;
var commands = null;
var create_del_req =
QuickModify.actions (
['a', '=== [[:' + title + ']] ===\n\n' + reason + ' \~\~\~\~\n']
, [(dont_save ? 'e' : (dont_close ? 's' : 'c')), 'Nominating [[:' + title + ']]']
);
if (same_window) {
revision = null;
}
var tag_img_action =
['i', '\{\{' + nfd_deleteTemplate
+ '|reason=' + reason
+ '|subpage=\{\{subst:FULLPAGENAME\}\}' // or '|subpage=' + title
+ '|day=\{\{subst:CURRENTDAY\}\}'
+ '|month=\{\{subst:CURRENTMONTHNAME\}\}'
+ '|year=\{\{subst:CURRENTYEAR\}\}'
+'\}\}\n'
];
if (revision) {
edit_lk = mw.config.get('wgServer') + wgScript + '?title=' + encodeURIComponent (title) + '&action=edit';
commands =
QuickModify.actions (
['h', revision]
, (dont_save ? tag_img_action : ['c'])
, (dont_save ? ['e', 'Nominating for deletion'] : null)
)
+ QuickModify.join (del_req, create_del_req);
} else {
edit_lk = mw.config.get('wgServer') + wgScript + '?title=' + encodeURIComponent (del_req) + '&action=edit';
commands = create_del_req;
} // end if;
commands = commands
+ ((revision && dont_save) || same_window
? "" // Modifications of page "title" already in revision ID check, since we don't save
: QuickModify.join (
title
, QuickModify.actions (
tag_img_action
, [(dont_save ? 'e' : (dont_close ? 's' : 'c')), 'Nominating for deletion']
)
)
)
+ (user ? QuickModify.join ( // Notify user, if we have a user
'User_talk:' + user
, QuickModify.actions (
['a', '\n\{\{' + nfd_idwTemplate + '|' + title + '\}\}\~\~\~\~']
, [(dont_save ? 'e' : (dont_close ? 's' : 'c')), '[[:' + title + ']] nominated for deletion']
)
)
: ""
)
+ QuickModify.join ( // Enter new deletion request in today's log page
del_req_log
, QuickModify.actions (
['a', '\{\{' + del_req + '\}\}']
, [(dont_save ? 'e' : 's'), 'Nominating [[:' + title + ']]'] // Never close, might have edit conflicts
)
);
QuickModify.execute (edit_lk, commands);
if (same_window) {
if (is_edit_page) {
// Just insert the deletion template and set the edit summary
var text = document.getElementById ('wpTextbox1');
if (text) {
text.value = tag_img_action[1] + text.value;
scrollTextArea (text, true);
setEditSummary ('Nominating for deletion');
}
} else {
// Modify file page in place. Quick'n'dirty fix for the "purge" problem (if the file page
// is saved before the DR page exists, the template will complain about an "incomplete
// deletion request", and the file page must be purged to rectify that): delay a bit,
// hoping that the creation of the DR is faster.
window.setTimeout (
function () {
QuickModify.execute (
mw.config.get('wgServer') + wgScript + '?title=' + encodeURIComponent (title) + '&action=edit'
, QuickModify.actions (
tag_img_action
, [(dont_save ? 'e' : 's'), 'Nominating for deletion'] // Never close, we didn't open it
)
, true
);
}
,notifier_timeout
);
} // end if is_edit_page
}
},
notify : function (text, user, edit_comment, dont_save, dont_close, same_window)
{
if (!text || text.length == 0) return;
if (!user || user.length == 0) user = Notifier.get_user_from_page ();
if (!user) return;
var edit_lk = mw.config.get('wgServer') + wgScript + '?title=User_talk:' + encodeURIComponent (user) + '&action=edit';
if (same_window) dont_close = true;
QuickModify.execute (
edit_lk
, QuickModify.actions (
['a', '\n' + text]
, (dont_save
? (edit_comment ? ['e', edit_comment] : null)
: (edit_comment ? [(dont_close ? 's' : 'c'), edit_comment]
: [(dont_close ? 's' : 'c')]
)
)
)
, same_window
);
},
notify_user : function ( text, user, edit_comment, dont_save, dont_close, same_window
, prompt, input_mandatory)
{
if (!text || text.length == 0) return;
if (!user || user.length == 0) {
if (mw.config.get('wgNamespaceNumber') == 2 || mw.config.get('wgNamespaceNumber') == 3) { // User or User talk
user = mw.config.get('wgTitle');
} else
user = Notifier.safe_prompt (notifier_user_query, "");
}
if (!user) return; // No user given: we're bust.
var is_edit_page = false;
if (mw.config.get('wgNamespaceNumber') == 3) {
same_window = same_window || (Notifier.strip_namespace (mw.config.get('wgPageName')) == user);
is_edit_page = (mw.config.get('wgAction') == 'edit' && document.getElementById ('editform') != null);
}
if ((text + ' ' + edit_comment).indexOf ('$1') > 0 && prompt && prompt.length > 0) {
var input = Notifier.safe_prompt (prompt, "");
if (input == null) return; // User cancelled
if (input.length == 0 && input_mandatory) return; // Mandatory input not given
text = replaceVars (text, '$', '1', input);
edit_comment = replaceVars (edit_comment, '$', '1', input);
}
if (is_edit_page) {
var textbox = document.getElementById ('wpTextbox1');
textbox.value = textbox.value + '\n' + text;
scrollTextArea (textbox, false);
setEditSummary (edit_comment);
// Never save on an open edit page!
} else {
Notifier.notify (text, user, edit_comment, dont_save, dont_close, same_window);
}
},
// Internal routines
safe_prompt : function (label, default_text)
{
var input = prompt (label, default_text || "");
if (input) input = input.replace(/^\s\s*/, "").replace(/\s\s*$/, ""); // Strip leading and trailing whitespace
if (input && input.length > 0) {
if ( typeof (TextCleaner) != 'undefined'
&& typeof (TextCleaner.sanitizeWikiText) == 'function')
{
input = TextCleaner.sanitizeWikiText (input, true);
}
}
return input;
},
get_user_from_page : function ()
{
if (mw.config.get('wgNamespaceNumber') != 6) return null;
var filehistory = getElementsByClassName (document, 'table', 'filehistory');
if (!filehistory || filehistory.length == 0) return null;
var uploaders = getElementsByClassName (filehistory[0], 'a', 'mw-userlink');
if (!uploaders || uploaders.length == 0) return null;
var user = Notifier.strip_namespace (Notifier.get_article_from_href (uploaders[0].href));
var i = 1;
while (user && (user == 'FlickreviewR' || user == 'Rotatebot' || user == 'Cropbot' || user == 'Picasa Review Bot') && i < uploaders.length) {
// FlickrevieweR uploaded original size from Flickr, or Rotatebot rotated. Try earlier entries.
// 2009-02-27 added Cropbot to the list of uploaders to ignore -- ChrisiPK
user = Notifier.strip_namespace (Notifier.get_article_from_href (uploaders[i].href));
i = i + 1;
}
uploaders = null;
if (user) {
var new_user = null;
if (user.replace (/ /g, '_') == 'Flickr_upload_bot') {
// Let's try to find the *real* uploader
uploaders = document.getElementById ('flickr_upload_bot_reviewer');
} else if (user == 'FlickrLickr') {
uploaders = document.getElementById ('fileinfotpl_rev');
if (uploaders && uploaders.parentNode.cells.length > 1)
uploaders = uploaders.parentNode.cells[1];
else
uploaders = null;
} else if (user.replace (/ /g, '_') == 'File_Upload_Bot_(Magnus_Manske)') {
uploaders = document.getElementById ('fileinfotpl_src');
if (uploaders && uploaders.nodeName.toLowerCase () == 'th') {
// Find the next TD
uploaders = uploaders.nextSibling;
while (uploaders && uploaders.nodeName.toLowerCase () != 'td')
uploaders = uploaders.nextSibling;
}
if (uploaders) uploaders = uploaders.getElementsByTagName ('a');
if (uploaders && uploaders.length > 1) {
for (var l = 0; l < uploaders.length; l++) {
if ( uploaders[l].href == 'http://tools.wikimedia.de/~magnus/commonshelper.php'
|| uploaders[l].href == 'http://toolserver.org/~magnus/geograph_org2commons.php') {
if (l > 0) {
new_user = Notifier.strip_namespace (Notifier.get_article_from_href (uploaders[l-1].href));
break;
}
} else if (/www\.flickr\.com\/photos\//.test (uploaders[l].href)) {
// Magnus' flickr2commons tool.
if (l+1 < uploaders.length) {
new_user = Notifier.strip_namespace (Notifier.get_article_from_href (uploaders[l+1].href));
break;
}
}
}
}
uploaders = null;
new_user = Notifier.fix_double_encoding (new_user); // Magnus's bot double encodes.
}
if (uploaders) {
uploaders = uploaders.getElementsByTagName ('a');
if (uploaders && uploaders.length > 0)
new_user = Notifier.strip_namespace (Notifier.get_article_from_href (uploaders[0].href));
}
if (new_user) user = new_user;
}
return user;
},
get_user_from_server : function (title, callback)
{
var request = sajax_init_object ();
if (!request) {
callback (null); return;
}
var api = mw.config.get('wgServer') + wgScriptPath + '/api.php';
var params =
'format=json&action=query&rawcontinue=&prop=imageinfo|revisions&iiprop=user'
+ '&rvdir=older&rvprop=content&iilimit=10&rvlimit=1&titles=' + encodeURIComponent (title);
request.open ('POST', api, true);
request.setRequestHeader ('Content-type', 'application/x-www-form-urlencoded');
request.setRequestHeader ('Content-length', params.length);
request.onreadystatechange =
function ()
{
if (request.readyState != 4) return;
if (request.status == 200 && request.responseText && request.responseText.indexOf ('{') == 0)
Notifier.get_user_from_json
(eval ('(' + request.responseText + ')'), callback);
else
callback (null);
};
request.send (params);
},
get_user_from_json : function (info, callback)
{
if (!info || !info.query || !info.query.pages) {
callback (null); return;
}
var page = null;
for (var p in info.query.pages) {
page = info.query.pages[p]; break;
}
if ( !page
|| !page.imageinfo || page.imageinfo.length == 0
|| !page.revisions || page.revisions.length == 0) {
callback (null); return;
}
var text = page.revisions[0];
var img_log = page.imageinfo;
var user = img_log[0].user;
var i = 1;
while (user && (user == 'FlickreviewR' || user == 'Rotatebot' || user == 'Cropbot' || user == 'Picasa Review Bot') && i < img_log.length) {
// FlickrevieweR uploaded original size from Flickr, or Rotatebot rotated.
// Try earlier entries.
user = img_log[i].user;
i = i + 1;
}
if (text) text = text["*"];
if (!text || !user) {
callback (user); return;
}
var match = null;
if (user.replace (/ /g, '_') == 'Flickr_upload_bot') {
// Check for the bot's upload template
match =
/\{\{User:Flickr upload bot\/upload(\|[^\|\}]*)?\|reviewer=([^\}]*)\}\}/.exec (text);
if (match) match = match[2];
} else if (user == 'File Upload Bot (Magnus Manske)') {
// CommonsHelper
match =
/transferred to Commons by \[\[User:([^\]\|]*)(\|([^\]]*))?\]\] using/.exec (text);
if (!match)
// geograph_org2commons, regex accounts for typo ("transferd") and its possible
// future correction
match =
/geograph.org.uk\]; transferr?e?d by \[\[User:([^\]\|]*)(\|([^\]]*))?\]\] using/.exec (text);
if (!match && /(www\.)?flickr\.com\/photos\//.test (text))
// flickr2commons
match = /\* Uploaded by \[\[User:([^\]\|]*)(\|([^\]]*))?\]\]/.exec (text);
if (match) match = match[1];
match = Notifier.fix_double_encoding (match);
} else if (user == 'FlickrLickr') {
match = /\n\|reviewer=\s*(.*)\n/.exec (text);
if (match) match = match[1];
}
if (match)
user = match.replace (/^\s\s*/, "").replace (/\s\s*$/, "");
callback (user);
},
fix_double_encoding : function (match)
{
if (!match) return match;
var utf8 = /[\u00C2-\u00F4][\u0080-\u00BF][\u0080-\u00BF]?[\u0080-\u00BF]?/g;
if (!utf8.test (match)) return match;
// Looks like we have a double encoding. At least it contains character
// sequences that might be legal UTF-8 encodings. Translate them into %-
// syntax and try to decode again.
var temp = "", curr = 0, m, hex_digit = "0123456789ABCDEF";
var str = match.replace (/%/g, '%25');
utf8.lastIndex = 0; // Reset regexp to beginning of string
try {
while ((m = utf8.exec (str)) != null) {
temp += str.substring (curr, m.index);
m = m[0];
for (var i = 0; i < m.length; i++) {
temp += '%'
+ hex_digit.charAt (m.charCodeAt (i) / 16)
+ hex_digit.charAt (m.charCodeAt (i) % 16);
}
curr = utf8.lastIndex;
}
if (curr < str.length) temp += str.substring (curr);
temp = decodeURIComponent (temp);
return temp;
} catch (e) {
}
return match;
},
get_article_from_href : function (href)
{
var match = /[?&]title=([^&]*)/.exec (href);
if (!match) {
match = new RegExp(wgArticlePath.replace ('$1', '(.*)$'));
match = match.exec (href);
}
if (match) return decodeURIComponent (match[1]);
return null;
},
strip_namespace : function (page_name)
{
if (!page_name) return page_name;
var title = page_name.substr (page_name.indexOf (':') + 1);
return title;
}
} // end Notifier
// Hook for localizations
if (wgUserLanguage != 'en') importScript ('MediaWiki:Notifier.js/' + wgUserLanguage);
} // end if (guard against double inclusion)
// </source>