MediaWiki:Gadget-PatrollingEnhancements.js: difference between revisions

From Wiktionary, the free dictionary
Jump to navigation Jump to search
Content deleted Content added
fix previous: it turns out that mediaWiki.user.tokens.get('deleteToken') doesn't work: only editToken, patrolToken, and watchToken do.
m indicate format of GPE.currMonth & GPE.lastMonth in a comment
(2 intermediate revisions by the same user not shown)
Line 23: Line 23:
==Automated patrolling (whitelisting)==
==Automated patrolling (whitelisting)==
<pre> */
<pre> */

// set GPE.currMonth and GPE.lastMonth (in the form of, e.g., '2013/February')
(
function ()
{
var monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November',
'December' ];

var now = new Date();

var currYear = now.getFullYear();
var currMonthNum = now.getMonth();

GPE.currMonth = currYear + '/' + monthNames[currMonthNum];

var lastYear = (currMonthNum == 0 ? currYear - 1 : currYear);
var lastMonthNum = (currMonthNum == 0 ? 11 : currMonthNum - 1);

GPE.lastMonth = lastYear + '/' + monthNames[lastMonthNum];
}
)();


GPE.individualWhiteListedPages =
GPE.individualWhiteListedPages =
Line 34: Line 56:
"Wiktionary:Tea room": true,
"Wiktionary:Tea room": true,
"Wiktionary:Etymology scriptorium": true,
"Wiktionary:Etymology scriptorium": true,
"Wiktionary:Beer parlour": true,
"Wiktionary:Grease pit": true,
"Wiktionary:Requested entries (English)": true,
"Wiktionary:Requested entries (English)": true,
"Wiktionary:Requested entries (Spanish)": true,
"Wiktionary:Requested entries (Spanish)": true,
Line 47: Line 67:
"Wiktionary:Word of the day/Nominations": true
"Wiktionary:Word of the day/Nominations": true
};
};

GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.currMonth] =
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.lastMonth] =
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.currMonth] =
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.lastMonth] =
true;


// per-user white-listed sub-pages (for example, edits by user Foo
// per-user white-listed sub-pages (for example, edits by user Foo

Revision as of 06:21, 24 February 2013

var GPE = {};

/* </pre>
==Configuration options==
<pre> */

// The initial value to put in the "deletion reason" text-field; you can
// override this in your common.js (or vector.js or whatnot).
GPE.initialDeleteReason = '';

// The value to use as a deletion reason if you leave the text-field blank; you
// can override it in your common.js (or vector.js or whatnot). If you *don't*
// override this, then MediaWiki will generate an automatic deletion reason that
// indicates the entry's last editor and the beginning of its content.
GPE.deleteReasonIfBlank = '';

// By DCDuring's request. If you set this to true, then Special:Watchlist will
// show the deletion-reason text-input, but *not* the deletion-reason dropdown,
// when there's an unpatrolled new-page-creation.
GPE.hideDeleteReasonDropdownOnWatchlist = false;

/* </pre>
==Automated patrolling (whitelisting)==
<pre> */

// set GPE.currMonth and GPE.lastMonth (in the form of, e.g., '2013/February')
(
  function ()
  {
    var monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June',
                       'July', 'August', 'September', 'October', 'November',
                       'December' ];

    var now = new Date();

    var currYear = now.getFullYear();
    var currMonthNum = now.getMonth();

    GPE.currMonth = currYear + '/' + monthNames[currMonthNum];

    var lastYear = (currMonthNum == 0 ? currYear - 1 : currYear);
    var lastMonthNum = (currMonthNum == 0 ? 11 : currMonthNum - 1);

    GPE.lastMonth = lastYear + '/' + monthNames[lastMonthNum];
  }
)();

GPE.individualWhiteListedPages =
{
  "Wiktionary:Requests for cleanup": true,
  "Wiktionary:Requests for verification": true,
  "Wiktionary:Requests for deletion": true,
  "Wiktionary:Requests for deletion/Others": true,
  "Wiktionary:Requests for moves, mergers and splits": true,
  "Wiktionary:Information desk": true,
  "Wiktionary:Tea room": true,
  "Wiktionary:Etymology scriptorium": true,
  "Wiktionary:Requested entries (English)": true,
  "Wiktionary:Requested entries (Spanish)": true,
  "Wiktionary:List of protologisms": true,
  "Wiktionary:Translation requests": true,
  "Wiktionary:Feedback": true,
  "Wiktionary:Sandbox": true,
  "Wiktionary talk:Sandbox": true,
  "Wiktionary:Tutorial (Editing)/sandbox": true,
  "Wiktionary:Featured word candidates": true,
  "Wiktionary:Word of the day/Nominations": true
};

GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.currMonth] =
  GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.lastMonth] =
  GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.currMonth] =
  GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.lastMonth] =
  true;

// per-user white-listed sub-pages (for example, edits by user Foo
// to User:Foo/vector.js should be autopatrolled):
GPE.perUserWhiteListedSubPages =
{
  "/Sandbox": true,
  "/sandbox": true,
  "/chick.js": true,
  "/chick.css": true,
  "/standard.js": true,
  "/standard.css": true,
  "/cologneblue.js": true,
  "/cologneblue.css": true,
  "/modern.js": true,
  "/modern.css": true,
  "/myskin.js": true,
  "/myskin.css": true,
  "/nostalgia.js": true,
  "/nostalgia.css": true,
  "/simple.js": true,
  "/simple.css": true,
  "/vector.js": true,
  "/vector.css": true,
  "/common.js": true,
  "/common.css": true
};

GPE.individualWhiteListedContributors =
{
  "16@r": true,
  "176.249.126.12": true,
  "71.66.97.228": true,
  "89.73.179.94": true
};

GPE.shouldAutoPatrol = function(link)
{
  var pagename = link.title;
  if(pagename.indexOf('User talk:') == 0)
    return true;
  if(pagename in GPE.individualWhiteListedPages)
    return true;

  var contributor;
  if(mediaWiki.config.get('wgCanonicalSpecialPageName') === 'Contributions')
  {
    contributor =
      document.getElementById('t-contributions')
              .getElementsByTagName('a')[0].href.replace(/.*\//, '');
  }
  else
  {
    var li = link.parentNode;
    if(li.tagName.toUpperCase() == 'SPAN')
      li = li.parentNode;
    var links = li.getElementsByTagName('a');
    for(var i = 0; i < links.length; ++i)
      if(links[i].title.indexOf('Special:Contributions/') == 0)
      {
        contributor = links[i].title.substr('Special:Contributions/'.length);
        break;
      }
  }
  if(pagename.indexOf('User:' + contributor + '/') == 0)
    if(pagename.substr(contributor.length + 5) in GPE.perUserWhiteListedSubPages)
      return true;
  if(contributor in GPE.individualWhiteListedContributors)
    return true;

  return false;
};

/* </pre>

==Utility functions==
<pre> */

GPE.newButton = function(text, color, hoverText)
{
  var button = newNode('button', text);
  button.style.background = color;
  button.style.color = '#FFF';
  button.style.border = '0';
  button.style.padding = '0';
  button.style.cursor = 'pointer';
  button.title = hoverText;
  return button;
};

GPE.disableButton = function(button, text, hoverText)
{
  button.onclick = null;
  button.title = (hoverText || '');
  button.innerHTML = text;
  // clear out explicit styling and disable, so we can get appropriate
  // disabled-button styles:
  button.style.background = '';
  button.style.color = '';
  button.style.cursor = '';
  button.disabled = 'disabled';
  // explicitly style like a disabled button:
  var computedStyle = window.getComputedStyle(button);
  button.style.background = computedStyle.backgroundColor;
  button.style.color = computedStyle.color;
  button.style.cursor = computedStyle.cursor;
  // re-enable, so the title will show up as hover-text in Firefox
  // (see https://bugzilla.mozilla.org/show_bug.cgi?id=274626):
  button.disabled = '';
  // technically the button is "enabled" now, but it *looks* disabled, and it
  // doesn't have an onclick event, so it's disabled for all normal purposes
};

GPE.anErrorOccurred = function(shortMsg, debugInfo)
{
  var msg, link;
  if(debugInfo)
  {
    link = newNode('a', {href:'#'}, 'here');
    msg =
      newNode
      (
        'p',
        'An error occurred in MediaWiki:Gadget-PatrollingEnhancements.js: ',
        newNode('tt', shortMsg),
        ' \u2014 click ',
        link,
        ' for debugging information.'
      );
  }
  else
    msg =
      newNode
      (
        'p',
        'An error occurred in MediaWiki:Gadget-PatrollingEnhancements.js: ',
        newNode('tt', shortMsg)
      );
  var bodyContent = $('div#bodyContent');
  bodyContent.insertBefore(msg, bodyContent.firstChild);
  if(link)
    link.onclick = function () { alert(debugInfo); };
  if(mediaWiki.config.get('wgUserName') == 'Ruakh')
    alert(shortMsg + '\n' + debugInfo);
};

/* </pre>
==Patrol-buttons==
<pre> */

GPE.addPatrolButton = function(link, rcid)
{
  var button = GPE.newButton('M', '#009', 'click to mark as patrolled');
  link.parentNode.insertBefore(button, link.nextSibling);
  link.parentNode.insertBefore(document.createTextNode(' · '), button);
  button.onclick =
    function ()
    {
      var token = mediaWiki.user.tokens.get('patrolToken');
      $.post
      (
        '/w/api.php?format=json&action=patrol',
        { token: token, rcid: rcid },
        function (data)
        {
          if(data.patrol)
            GPE.disableButton(button, 'm', 'marked as patrolled');
          else if(data.error)
          {
            var msg = data.error.code + ': ' + data.error.info;
            if(data.error.code == 'badtoken')
              msg += ': "' + token + '"';
            alert(msg);
          }
        },
        'json'
      );
    };
  if(GPE.shouldAutoPatrol(link))
    button.click();

  // remove the exclamation points:
  var abbreviations = link.parentNode.getElementsByTagName('abbr');
  for(var i = abbreviations.length - 1; i >= 0; --i)
    if((' '+abbreviations[i].className+' ').indexOf(' unpatrolled ') > -1)
      abbreviations[i].parentNode.removeChild(abbreviations[i]);
};


addOnloadHook
(
  function ()
  {
    if(mediaWiki.config.get('wgPageName') != 'Special:RecentChanges')
      if(mediaWiki.config.get('wgPageName') != 'Special:NewPages')
        if(mediaWiki.config.get('wgPageName') != 'Special:Watchlist')
          if(mediaWiki.config.get('wgAction') != 'markpatrolled')
            if(mediaWiki.config.get('wgAction') != 'delete')
              if(mediaWiki.config.get('wgAction') != 'rollback')
                return;
    var links =
      document.getElementById('bodyContent').getElementsByTagName('a');
    for(var i = links.length - 1; i >= 0; --i)
    {
      var link = links[i];
      if(link.href.search(/&rcid=\d+/) == -1)
        continue;
      var rcid = /&rcid=(\d+)/.exec(link.href)[1];
      GPE.addPatrolButton(link, rcid);
    }
  }
);

/* </pre>

==Delete-buttons==
<pre> */

GPE.addDeleteButton = function(link, title)
{
  var button = GPE.newButton('D', '#900', 'click to delete');
  link.parentNode.insertBefore(button, link.nextSibling);
  link.parentNode.insertBefore(document.createTextNode(' · '), button);
  button.onclick =
    function ()
    {
      var dropdownReason =
        document.getElementById('deleteReasonsDropdown')
          ? document.getElementById('deleteReasonsDropdown').value
          : '';
      if(dropdownReason == 'other')
        dropdownReason = '';
      var textInputReason =
        document.getElementById('deleteReasonTextInput').value;
      var reason;
      if(dropdownReason.length && textInputReason.length)
        reason = dropdownReason + ': ' + textInputReason;
      else if(dropdownReason.length || textInputReason.length)
        reason = dropdownReason + textInputReason;
      else
        reason = GPE.deleteReasonIfBlank;
      var token = mediaWiki.user.tokens.get('deleteToken');
      $.post
      (
        '/w/api.php?format=json&action=delete',
        { title: title, token: token, reason: reason },
        function (data)
        {
          if(data['delete'])
            GPE.disableButton(button, 'd', 'deleted');
          else if(data.error)
          {
            var msg = data.error.code + ': ' + data.error.info;
            if(data.error.code == 'badtoken')
              msg += ': "' + token + '"';
            alert(msg);
          }
        },
        'json'
      );
    };
};

GPE.addDeleteReasonInput = function()
{ var deleteReasonDiv =
    ( newNode
      ( 'div',
        { style:
            'background:#900; color:#FFF; ' +
            'position:fixed; bottom:0; right:0; margin-bottom:0'
        },
        '\u00A0Deletion reason:\u00A0'
      )
    );
  deleteReasonDiv.title =
    'the deletion reason (message/summary) to use when you click "D"';
  var deleteReasonTextInput =
  ( newNode
    ( 'input',
      { type: 'text',
        size: 80,
        id: 'deleteReasonTextInput',
        value: GPE.initialDeleteReason,
        style: 'position:fixed; right:0; margin-bottom:0'
      }
    )
  );
  deleteReasonDiv.appendChild(deleteReasonTextInput);
  document.getElementById('bodyContent').appendChild(deleteReasonDiv);

  if(GPE.hideDeleteReasonDropdownOnWatchlist)
    if(mediaWiki.config.get('wgPageName') == 'Special:Watchlist')
      return;

  // get canned messages from [[MediaWiki:Deletereason-dropdown]]:
  $.getJSON
  ( '/w/api.php?format=json&action=query&meta=allmessages&ammessages=Deletereason-dropdown',
    function (data)
    { var rawDeleteReasons = data.query.allmessages[0]['*'];
      var deleteReasonsDropdown =
        newNode('select',
          { id: 'deleteReasonsDropdown', style: 'vertical-align: bottom' });
      deleteReasonsDropdown.appendChild
        (newNode('option', { value: 'other' }, 'Other reason'));
      var optGroup = deleteReasonsDropdown;
      rawDeleteReasons.replace
      ( /^(\*\*?) *(.+)$/gm,
        function (s, asterisks, text)
        { if(asterisks == '*')
            deleteReasonsDropdown.appendChild
              (optGroup = newNode('optgroup', { label: text }));
          else // '**'
            optGroup.appendChild(newNode('option', { value: text }, text));
        }
      );
      deleteReasonDiv.insertBefore(deleteReasonsDropdown, deleteReasonTextInput);
      deleteReasonDiv.insertBefore(newNode('br'), deleteReasonTextInput);
      deleteReasonDiv.insertBefore(document.createTextNode('\u00A0'), deleteReasonTextInput);
    }
  );
};

addOnloadHook
( function ()
  {
    if(mediaWiki.config.get('wgPageName') != 'Special:RecentChanges')
      if(mediaWiki.config.get('wgPageName') != 'Special:NewPages')
        if(mediaWiki.config.get('wgPageName') != 'Special:Watchlist')
          if(mediaWiki.config.get('wgAction') != 'markpatrolled')
            if(mediaWiki.config.get('wgAction') != 'delete')
              if(mediaWiki.config.get('wgAction') != 'rollback')
                return;
    $.getJSON
    (
      '/w/api.php?format=json&action=tokens&type=delete',
      function (data)
      {
        var token = data.tokens.deletetoken;
        if(! token || token.search(/^[0-9a-f]{32}\+\\$/) != 0)
          return;
        mediaWiki.user.tokens.set('deleteToken', token);

        var links =
          document.getElementById('bodyContent').getElementsByTagName('a');
        var shouldAddDeleteReasonInput = false;
        for(var i = links.length - 1; i >= 0; --i)
        {
          var link = links[i];
          if(link.href.search(/#/) == -1) // skip within-page links (don't ask . . .)
            if(link.href.search(/&rcid=\d+/) > -1) // only want patrolled edits
              if(link.href.search(/&diff=\d+/) == -1) // only want new pages
              {
                GPE.addDeleteButton(link, link.title);
                shouldAddDeleteReasonInput = true;
              }
        }
        if(shouldAddDeleteReasonInput)
          GPE.addDeleteReasonInput();
      }
    );
  }
);

/* </pre>

==Special:Contributions==
<pre> */

addOnloadHook
(
  function ()
  {
    if(mediaWiki.config.get('wgPageName').search(/^Special:Contributions(\/|$)/) != 0) 
      return;
    var user = document.getElementById('t-contributions').firstChild.href.replace(/^.*?\/Special:Contributions\//, '');
    $.getJSON
    (
      '/w/api.php?format=json&action=query&list=recentchanges&rcuser=' + user +
        '&rcprop=ids|title&rcshow=!patrolled&rclimit=500&rctype=edit|new',
      function (data)
      {
        data = data.query.recentchanges;
        if(data.length == 0)
          return;
        var map = {};
        for(var i = 0; i < data.length; ++i)
          if(data[i].type == 'edit')
            map[''+data[i].revid] = data[i].rcid;
          else // 'new'
            map[data[i].title] = data[i].rcid;
        // first, handle the edit-links:
        var links =
          document.getElementById('bodyContent').getElementsByTagName('a');
        for(var i = links.length - 1; i >= 0; --i)
        {
          var mapKey = /&diff=prev&oldid=(\d+)/.exec(links[i].href);
          if(mapKey && map[mapKey[1]])
          {
            GPE.addPatrolButton(links[i], map[mapKey[1]]);
          }
        }
        // then, handle the new-links:
        var abbrs =
          document.getElementById('bodyContent').getElementsByTagName('abbr');
        for(var i = abbrs.length - 1; i >= 0; --i)
        {
          if(abbrs[i].className != 'newpage')
            continue;
          var link = abbrs[i];
          while(link && link.nodeName.toUpperCase() != 'A')
            link = link.nextSibling;
          if(link && link.title && map[link.title])
          {
            GPE.addPatrolButton(link, map[link.title]);
            if(link.href.indexOf('?') == -1)
              link.href = link.href + '?rcid=' + map[link.title];
            else
              link.href = link.href + '&rcid=' + map[link.title];
          }
        }
 
        // can't add the delete-links until after we've done the above, because we
        // only want to add delete-links to unpatrolled edits:
        var shouldAddDeleteReasonInput = false;
        var abbrs =
          document.getElementById('bodyContent').getElementsByTagName('abbr');
        for(var i = abbrs.length - 1; i >= 0; --i)
        {
          if(abbrs[i].className != 'newpage')
            continue;
          var link = abbrs[i];
          while(link && link.nodeName.toUpperCase() != 'A')
            link = link.nextSibling;
          if(! link) // shouldn't really happen
            continue;
          if(link.href.search(/\?(.*&)?rcid=\d+$/) == -1)
            continue;
          GPE.addDeleteButton(link, link.title);
          shouldAddDeleteReasonInput = true;
        }
        if(shouldAddDeleteReasonInput)
          GPE.addDeleteReasonInput();
      }
    );
  }
);