Skip to content

Instantly share code, notes, and snippets.

@atlight
Last active December 15, 2015 21:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atlight/5328823 to your computer and use it in GitHub Desktop.
Save atlight/5328823 to your computer and use it in GitHub Desktop.
diff --git a/modules/friendlytag.js b/modules/friendlytag.js
index d4a272b..be47f25 100644
--- a/modules/friendlytag.js
+++ b/modules/friendlytag.js
@@ -22,18 +22,49 @@ Twinkle.tag = function friendlytag() {
$(twAddPortletLink("#", "Tag", "friendly-tag", "Add maintenance tags to file", "")).click(Twinkle.tag.callback);
}
// article tagging
- else if( mw.config.get('wgNamespaceNumber') === 0 && mw.config.get('wgCurRevisionId') ) {
+ else if( mw.config.get('wgNamespaceNumber') === 0 && mw.config.get('wgCurRevisionId') && mw.config.get('wgAction') === "view" ) {
Twinkle.tag.mode = 'article';
- $(twAddPortletLink("#", "Tag", "friendly-tag", "Add maintenance tags to article", "")).click(Twinkle.tag.callback);
+ $(twAddPortletLink("#", "Tag", "friendly-tag", "Add or remove maintenance tags to/from article", "")).click(Twinkle.tag.callback);
}
// tagging of draft articles
else if( ((mw.config.get('wgNamespaceNumber') === 2 && mw.config.get('wgPageName').indexOf("/") !== -1) || /^Wikipedia\:Articles[ _]for[ _]creation\//.exec(mw.config.get('wgPageName')) ) && mw.config.get('wgCurRevisionId') ) {
Twinkle.tag.mode = 'draft';
- $(twAddPortletLink("#", "Tag", "friendly-tag", "Add review tags to draft article", "")).click(Twinkle.tag.callback);
+ $(twAddPortletLink("#", "Tag", "friendly-tag", "Add or remove review tags to/from draft article", "")).click(Twinkle.tag.callback);
}
};
+Twinkle.tag.foundTags = [];
+Twinkle.tag.foundTagsFromMultipleIssues = [];
+
Twinkle.tag.callback = function friendlytagCallback( uid ) {
+ // check for existing tags on article using HTML classes
+ if (Twinkle.tag.mode === "article") {
+ Twinkle.tag.foundTags = [];
+ $("table.ambox").each(function() {
+ var classes = $(this).attr("class").split(" ");
+ if (classes.length === 5 && classes[4].indexOf("ambox-") === 0) {
+ var className = classes[4].substring(6);
+ // translate the class name into a tag name we can deal with
+ // if Twinkle knows about it, push it
+ var normalizedTag = Twinkle.tag.article.amboxClassesToCanonicalNames[className];
+ if (normalizedTag) {
+ if (normalizedTag === "multiple issues") {
+ // same deal for the {{multiple issues}} list items
+ $(this).find("li").each(function() {
+ var normalizedIssue;
+ if (normalizedIssue = Twinkle.tag.article.multipleIssuesIDsToCanonicalNames[$(this).attr("id").substring(22)]) {
+ Twinkle.tag.foundTags.push(normalizedIssue);
+ Twinkle.tag.foundTagsFromMultipleIssues.push(normalizedIssue); // used when parsing page text
+ }
+ });
+ } else {
+ Twinkle.tag.foundTags.push(normalizedTag);
+ }
+ }
+ }
+ });
+ }
+
var Window = new SimpleWindow( 630, (Twinkle.tag.mode === "article") ? 450 : 400 );
Window.setScriptName( "Twinkle" );
// anyone got a good policy/guideline/info page/instructional page link??
@@ -43,7 +74,7 @@ Twinkle.tag.callback = function friendlytagCallback( uid ) {
switch( Twinkle.tag.mode ) {
case 'article':
- Window.setTitle( "Article maintenance tagging" );
+ Window.setTitle( "Add or remove article maintenance tags" );
form.append( {
type: 'checkbox',
@@ -71,6 +102,10 @@ Twinkle.tag.callback = function friendlytagCallback( uid ) {
]
});
+ if (Twinkle.tag.foundTags.length > 0) {
+ form.append( { type: 'div', label: [ "Tags in ", htmlNode("b", "green", "green"), " are already present on the article." ] } );
+ }
+
form.append( { type: 'div', id: 'tagWorkArea' } );
if( Twinkle.getFriendlyPref('customTagList').length ) {
@@ -114,7 +149,7 @@ Twinkle.tag.callback = function friendlytagCallback( uid ) {
break;
case 'draft':
- Window.setTitle( "Article draft tagging" );
+ Window.setTitle( "Add or remove article draft tags" );
form.append({ type: 'header', label:'Draft article tags' });
form.append({ type: 'checkbox', name: 'draftTags', list: Twinkle.tag.draftList });
@@ -149,6 +184,7 @@ Twinkle.tag.updateSortOrder = function(e) {
if (!Twinkle.tag.checkedTags) {
Twinkle.tag.checkedTags = [];
}
+ Twinkle.tag.checkedTags = Twinkle.tag.checkedTags.concat(Twinkle.tag.foundTags);
// function to generate a checkbox, with appropriate subgroup if needed
var makeCheckbox = function(tag, description) {
@@ -249,9 +285,9 @@ Twinkle.tag.updateSortOrder = function(e) {
var rendered = div.render();
$workarea.replaceWith(rendered);
- var $rendered = $(rendered);
- $rendered.find("h5").css({ 'font-size': '110%', 'margin-top': '1em' });
- $rendered.find("div").filter(":has(span.quickformDescription)").css({ 'margin-top': '0.4em' });
+ $workarea = $(rendered);
+ $workarea.find("h5").css({ 'font-size': '110%', 'margin-top': '1em' });
+ $workarea.find("div").filter(":has(span.quickformDescription)").css({ 'margin-top': '0.4em' });
}
// alphabetical sort order
else {
@@ -266,6 +302,25 @@ Twinkle.tag.updateSortOrder = function(e) {
});
$workarea.empty().append(tags.render());
}
+
+ // highlight tags already present on the article
+ var toggleHighlight = function(e) {
+ var $obj = $(e.target);
+ if (e.target.checked) {
+ $obj.next().css("color", "green");
+ } else {
+ $obj.next().css("color", "red");
+ }
+ };
+ $(Twinkle.tag.foundTags).each(function(k, v) {
+ var $input = $workarea.find('input[name="articleTags"][value="' + v + '"]');
+ $input.next().css({ "font-weight": "bold", "color": "green" });
+ $input.click(toggleHighlight);
+ if (v === "new unreviewed article") {
+ $input.attr("checked", false);
+ $input.next().css({ "color": "red" });
+ }
+ });
};
@@ -286,7 +341,7 @@ Twinkle.tag.article.tags = {
"cat improve": "article may require additional categories",
"citation style": "article has unclear or inconsistent inline citations",
"cleanup": "article may require cleanup",
- "cleanup-reorganize": "article may be in need of reorganization to comply with Wikipedia's layout guidelines",
+ "cleanup-reorganize": "article may be in need of reorganization",
"close paraphrasing": "article contains close paraphrasing of a non-free copyrighted source",
"COI": "article creator or major contributor may have a conflict of interest",
"condense": "article may have too many section headers dividing up its content",
@@ -328,8 +383,7 @@ Twinkle.tag.article.tags = {
"orphan": "article is linked to from few or no other articles",
"out of date": "article needs out-of-date information removed or updated",
"overcoverage": "article has an extensive bias or disproportional coverage towards one or more specific regions",
- "overlinked": "article may have too many duplicate and/or irrelevant links",
- "over detailed": "article contains an excessive amount of intricate detail",
+ "overly detailed": "article contains an excessive amount of intricate detail",
"peacock": "article may contain peacock terms that promote the subject without adding information",
"plot": "plot summary in article is too long",
"POV": "article does not maintain a neutral point of view",
@@ -412,7 +466,7 @@ Twinkle.tag.article.tagCategories = {
"context",
"expert-subject",
"metricate",
- "over detailed"
+ "overly detailed"
],
"Timeliness": [
"out of date",
@@ -451,7 +505,6 @@ Twinkle.tag.article.tagCategories = {
"Links": [
"dead end",
"orphan",
- "overlinked",
"wikify" // this tag is listed twice because it used to focus mainly on links, but now it's a more general cleanup tag
],
"Referencing technique": [
@@ -473,11 +526,309 @@ Twinkle.tag.article.tagCategories = {
"Informational": [
"GOCEinuse",
"in use",
- "new unreviewed article",
+ "new unreviewed article", // special-cased code
"under construction"
]
};
+// Here are some "translation tables", a necessary evil of this rather hackish detection system:
+// 1. Ambox class identifiers -> Twinkle "canonical" tag names
+// 2. {{multiple issues}} issue IDs -> "canonical" names
+// 3. Synonyms recognised by {{multiple issues}} -> "canonical" names
+
+Twinkle.tag.article.amboxClassesToCanonicalNames = {
+ "multiple_issues": "multiple issues",
+ "Advert": "advert",
+ "all_plot": "allplot",
+ "autobiography": "autobiography",
+ "BLP_sources": "BLP sources",
+ "BLP_unsourced": "BLP unsourced",
+ "capitalization": "capitalization",
+ "cat_improve": "cat improve",
+ "citation_style": "citation style",
+ "Cleanup": "cleanup",
+ "cleanup-reorganize": "cleanup-reorganize",
+ "close_paraphrasing": "close paraphrasing",
+ "COI": "COI",
+ "condense": "condense",
+ "confusing": "confusing",
+ "Context": "context",
+ "Copy_edit": "copy edit",
+ "Copypaste": "copypaste",
+ "dead_end": "dead end",
+ "disputed": "disputed",
+ "essay-like": "essay-like",
+ "Expert-subject": "expert-subject",
+ "external_links": "external links",
+ "fanpov": "fansite",
+ "fiction": "fiction",
+ "globalize": "globalize",
+ "GOCEinuse": "GOCEinuse",
+ "hoax": "hoax",
+ "in-universe": "in-universe",
+ "incoherent": "incoherent",
+ "In_use": "in use",
+ "lead_missing": "lead missing",
+ "lead_rewrite": "lead rewrite",
+ "lead_too_long": "lead too long",
+ "lead_too_short": "lead too short",
+ "cleanup-link_rot": "linkrot",
+ //"": "merge", -- these three use {{ombox}}, which lacks a class parameter
+ //"": "merge from",
+ //"": "merge to",
+ "metricate": "metricate",
+ "More_footnotes": "more footnotes",
+ "new_unreviewed_article": "new unreviewed article",
+ "No_footnotes": "no footnotes",
+ "non-free": "non-free",
+ "NOT": "NOT",
+ "Notability": "notability",
+ "not_English": "not English",
+ "one_source": "one source",
+ "Original_research": "original research",
+ "Orphan": "orphan",
+ "out_of_date": "out of date",
+ "over_coverage": "overcoverage",
+ "overly_detailed": "overly detailed",
+ "peacock": "peacock",
+ "Plot": "plot",
+ "POV": "POV",
+ "Primary_sources": "primary sources",
+ "Prose": "prose",
+ "puffery": "puffery",
+ "Recentism": "recentism",
+ "Refimprove": "ref improve",
+ "rough_translation": "rough translation",
+ "sections": "sections",
+ "self-published": "self-published",
+ "technical": "technical",
+ "cleanup-tense": "tense",
+ "Tone": "tone",
+ "too_few_opinions": "too few opinions",
+ "uncategorized": "uncategorized",
+ //"": "under construction", -- uses {{ombox}}, which lacks a class parameter
+ "Unreferenced": "unreferenced",
+ "unreliable_sources": "unreliable sources",
+ "Update": "update",
+ "very_long": "very long",
+ "Weasel": "weasel",
+ "Wikify": "wikify"
+};
+
+// Parameters that exist, but that are not known to friendlytag, are assigned |false|
+
+Twinkle.tag.article.multipleIssuesIDsToCanonicalNames = {
+ "advert": "advert",
+ "autobiography": "autobiography",
+ "BLP_IMDb_refimprove": false,
+ "BLP_IMDb-only_refimprove": false,
+ "BLP_sources": "BLP sources",
+ "BLP_unsourced": "BLP unsourced",
+ "citation_style": "citation style",
+ "cite_check": false,
+ "cleanup": "cleanup",
+ "cleanup-laundry": false,
+ "cleanup-link_rot": "linkrot",
+ "cleanup-reorganize": "cleanup-reorganize",
+ "cleanup-rewrite": false,
+ "COI": "COI",
+ "colloquial": false,
+ "confusing": "confusing",
+ "context": "context",
+ "contradict": false,
+ "copy_edit": "copy edit",
+ "criticisms": false,
+ "crystal": false,
+ "dead_end": "dead end",
+ "disputed": "disputed",
+ "do-attempt": false,
+ "essay": "essay-like",
+ "example_farm": false,
+ "expert": "expert-subject",
+ "external_links": "external links",
+ "fansite": "fansite",
+ "fiction": "fiction",
+ "game_guide": false,
+ "globalize": "globalize",
+ "histinfo": false,
+ "hoax": "hoax",
+ "howto": false,
+ "inappropriate_person": false,
+ "incomplete": false,
+ "in-universe": "in-universe",
+ "lead_missing": "lead missing",
+ "lead_rewrite": "lead rewrite",
+ "lead_too_long": "lead too long",
+ "lead_too_short": "lead too short",
+ "like_resume": false,
+ "news_release": false,
+ "no_footnotes": "no footnotes",
+ "NOT": "NOT",
+ "notability": "notability",
+ "one_source": "one source",
+ "original_research": "original research",
+ "orphan": "orphan",
+ "out_of_date": "out of date",
+ "overly_detailed": "overly detailed",
+ "peacock": "peacock",
+ "plot": "plot",
+ "POV": "POV",
+ "POV-check": false,
+ "primary_sources": "primary sources",
+ "prose": "prose",
+ "quote_farm": false,
+ "recentism": "recentism",
+ "refimprove": "ref improve",
+ "review": false,
+ "sections": "sections",
+ "self-published": "self-published",
+ "spam": false,
+ "story": false,
+ "synthesis": false,
+ "technical": "technical",
+ "tone": "tone",
+ "travel_guide": false,
+ "trivia": false,
+ "unbalanced": false,
+ "unreferenced": "unreferenced",
+ "unreliable_sources": "unreliable sources",
+ "update": "update",
+ "very_long": "very long",
+ "weasel": "weasel",
+ "wikify": "wikify"
+};
+
+Twinkle.tag.article.multipleIssuesParamsToCanonicalNames = {
+ "advert": "advert",
+ "autobiography": "autobiography",
+ "BLP IMDb refimprove": false,
+ "BLP IMDB refimprove": false,
+ "BLP IMDb-only refimprove": false,
+ "BLP IMDB-only refimprove": false,
+ "BLP sources": "BLP sources",
+ "BLP unsourced": "BLP unsourced",
+ "BLPsources": "BLP sources",
+ "BLPunsourced": "BLP unsourced",
+ "citation style": "citation style",
+ "citations missing": "ref improve",
+ "citationstyle": "citation style",
+ "citation-style": "citation style",
+ "cite check": false,
+ "citecheck": false,
+ "cleanup": "cleanup",
+ "cleanup-laundry": false,
+ "cleanup-link rot": "linkrot",
+ "Cleanup-link rot": "linkrot",
+ "cleanup-reorganize": "cleanup-reorganize",
+ "cleanup-rewrite": false,
+ "cleanup-spam": false,
+ "coi": "COI",
+ "COI": "COI",
+ "colloquial": false,
+ "confusing": "confusing",
+ "context": "context",
+ "contradict": false,
+ "copy edit": "copy edit",
+ "copyedit": "copy edit",
+ "criticism section": false,
+ "criticisms": false,
+ "crystal": false,
+ "dead end": "dead end",
+ "deadend": "dead end",
+ "disputed": "disputed",
+ "do-attempt": false,
+ "essay": "essay-like",
+ "essay-like": "essay-like",
+ "example farm": false,
+ "examplefarm": false,
+ "expert": "expert-subject",
+ "external links": "external links",
+ "fancruft": "overly detailed",
+ "fanpov": "fansite",
+ "fansite": "fansite",
+ "fiction": "fiction",
+ "game guide": false,
+ "gameguide": false,
+ "globalize": "globalize",
+ "histinfo": "histinfo",
+ "hoax": "hoax",
+ "howto": false,
+ "inappropriate person": false,
+ "inappropriate tone": "tone",
+ "incomplete": false,
+ "intro length": "lead too long",
+ "intromissing": "lead missing",
+ "introrewrite": "lead rewrite",
+ "intro-toolong": "lead too long",
+ "intro-tooshort": "lead too short",
+ "in-universe": "in-universe",
+ "jargon": "technical",
+ "laundry": false,
+ "laundrylists": false,
+ "lead missing": "lead missing",
+ "lead rewrite": "lead rewrite",
+ "lead too long": "lead too long",
+ "lead too short": "lead too short",
+ "like resume": false,
+ "likeresume": false,
+ "linkrot": "linkrot",
+ "long": "very long",
+ "news release": false,
+ "newsrelease": false,
+ "no footnotes": "no footnotes",
+ "NOT": "NOT",
+ "notability": "notability",
+ "notable": "notability",
+ "npov": "POV",
+ "NPOV": "POV",
+ "one source": "one source",
+ "onesource": "one source",
+ "organize": "cleanup-reorganize",
+ "original research": "original research",
+ "orphan": "orphan",
+ "out of date": "out of date",
+ "overly detailed": "overly detailed",
+ "peacock": "peacock",
+ "plot": "plot",
+ "pov": "POV",
+ "POV": "POV",
+ "pov-check": false,
+ "POV-check": false,
+ "primary sources": "primary sources",
+ "primarysources": "primary sources",
+ "prose": "prose",
+ "quote farm": false,
+ "quotefarm": false,
+ "recentism": "recentism",
+ "refimprove": "ref improve",
+ "restructure": "cleanup-reorganize",
+ "review": false,
+ "rewrite": false,
+ "sections": "sections",
+ "self-published": "self-published",
+ "spam": false,
+ "story": false,
+ "synthesis": false,
+ "technical": "technical",
+ "tone": "tone",
+ "travel guide": false,
+ "travelguide": false,
+ "trivia": false,
+ "unbalanced": false,
+ "unencyclopedic": "NOT",
+ "unref": "unreferenced",
+ "unreferenced": "unreferenced",
+ "unreliable sources": "unreliable sources",
+ "unreliable": "unreliable sources",
+ "update": "update",
+ "very long": "very long",
+ "verylong": "very long",
+ "weasel": "weasel",
+ "wikify": "wikify"
+};
+
+
+
// Tags for REDIRECTS start here
Twinkle.tag.spellingList = [
@@ -773,7 +1124,7 @@ Twinkle.tag.groupHash = [
'orphan',
'do-attempt',
'out of date',
- 'over detailed',
+ 'overly detailed',
'fancruft',
'peacock',
'plot',
@@ -817,74 +1168,203 @@ Twinkle.tag.groupHash = [
];
Twinkle.tag.callbacks = {
- main: function( pageobj ) {
+ main: function friendlytagCallbacksMain( pageobj ) {
var params = pageobj.getCallbackParameters();
- var tagRe, tagText = '', summaryText = 'Added';
- var tags = [], groupableTags = [];
-
- // Remove tags that become superfluous with this action
- var pageText = pageobj.getPageText().replace(/\{\{\s*(New unreviewed article|Userspace draft)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, "");
-
+ var tags = [], tagText = '';
+ var summaryTagsRemoved = [], summaryTagsAdded = [];
var i;
- if( Twinkle.tag.mode !== 'redirect' ) {
- // Check for preexisting tags and separate tags into groupable and non-groupable arrays
- for( i = 0; i < params.tags.length; i++ ) {
- tagRe = new RegExp( '(\\{\\{' + params.tags[i] + '(\\||\\}\\}))', 'im' );
+
+ // Remove "Userspace draft" tag, which becomes superfluous with this action
+ // (when present on the article, the "New unreviewed article" tag is automatically
+ // de-selected - i.e. defaults to removal - in the tag dialog)
+ var pageText = pageobj.getPageText().replace(/\{\{\s*Userspace draft\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, "");
+
+ // these arrays, along with params.tagsToRemove, pose a redundancy issue:
+ // a tag should only be in one of the five - if somehow it gets in more than one,
+ // behaviour is undefined
+ var groupableTagsToKeep = [], ungroupableTagsToKeep = [], groupableTagsToAdd = [], ungroupableTagsToAdd = [];
+ var tagsConvertedToMIParams = []; // to be removed
+
+ if( Twinkle.tag.mode === 'article' ) {
+ // multipleissues detection regex
+ // note: some scarcely-used redirects (AI, ai, MI, mi, Many issues, Issues) were left out for performance reasons
+ var miRegex = /\s*(?:((?:\s*\{\{\s*((?:multiple issues|multiple issues\/sandbox|article issues|multipleissues|articleissues|multiple)?)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}))\s*)/im;
+ var miResult = miRegex.exec(pageText);
+
+ // Article tagging: preliminary checks
+ var isTagGroupable = function(tag) {
+ return (params.group && Twinkle.tag.groupHash.indexOf(tag) !== -1 &&
+ (tag !== 'globalize' || params.globalizeSubcategory === 'globalize' ) &&
+ (tag !== 'notability' || params.notabilitySubcategory === 'none' )); // don't add to multipleissues for globalize/notability subcats
+ };
+
+ // "The Magic Tag Sorter"
+ // Check for pre-existing tags and separate tags into groupable and non-groupable arrays
+ $.each(params.tags, function(index, tag) {
+ var tagRe = new RegExp( '(\\{\\{' + tag + '(\\||\\}\\}))', 'im' );
if( !tagRe.exec( pageText ) ) {
- if( Twinkle.tag.groupHash.indexOf(params.tags[i]) !== -1 &&
- (params.tags[i] !== 'globalize' || params.globalizeSubcategory === 'globalize' ) &&
- (params.tags[i] !== 'notability' || params.notabilitySubcategory === 'none' )) {
- // don't add to multipleissues for globalize/notability subcats
- groupableTags = groupableTags.concat( params.tags[i] );
+ if (isTagGroupable(tag)) {
+ groupableTagsToAdd.push(tag);
} else {
- tags = tags.concat( params.tags[i] );
+ ungroupableTagsToAdd.push(tag);
}
} else {
- Status.info( 'Info', 'Found {{' + params.tags[i] +
- '}} on the article already...excluding' );
+ Status.info( 'Info', 'Found {{' + tag + '}} on the article already... excluding' );
}
- }
+ });
- if( params.group && groupableTags.length >= 3 ) {
- Status.info( 'Info', 'Grouping supported tags into {{multiple issues}}' );
+ $.each(params.tagsToKeep, function(index, tag) {
+ //var tagRe = new RegExp( '(\\{\\{' + tag + '(\\||\\}\\}))', 'im' );
+ //if( !tagRe.exec( pageText ) ) {
+ //if (confirm("The tag {{" + tag + "}} is no longer present on the article. \nClick OK to re-add the tag, or click Cancel to skip this tag.")) {
+ // if (isTagGroupable(tag)) {
+ // groupableTagsToAdd.push(tag);
+ // } else {
+ // ungroupableTagsToAdd.push(tag);
+ // }
+ //}
+ //} else {
+ if (isTagGroupable(tag)) {
+ groupableTagsToKeep.push(tag);
+ } else {
+ ungroupableTagsToKeep.push(tag);
+ }
+ //}
+ });
+
+ tags = ungroupableTagsToAdd;
+
+ // are there enough to group?
+ // if so, add {{multiple issues}}, or add parameters to the existing one
+ if (groupableTagsToAdd.length + groupableTagsToKeep.length >= 3) {
+ if (miResult) {
+ // {{multiple issues}} is already present
+ Status.info( 'Info', 'Adding supported tags to the {{multiple issues}} tag already present on the page' );
+
+ pageText = pageText.replace(miRegex, "\n");
+
+ tagText += "{{multiple issues";
+
+ // add parameters that were already present, and remove ones that were deselected
+ $.each(miResult[3].split("|").slice(1), function(k, tag) {
+ var paramName = tag.substring(0, tag.indexOf("=")).trim();
+ // normalize param name
+ var normalParamName = Twinkle.tag.article.multipleIssuesParamsToCanonicalNames[paramName];
+ if (!normalParamName || params.tagsToRemove.indexOf(normalParamName) !== -1) {
+ tagText += "|" + tag;
+ } else {
+ // XXX summary text
+ }
+ });
+
+ // then, add new tags
+ $.each(groupableTagsToAdd, function(k, tag) {
+ tagText += '|' + tag + '={{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}';
+ // XXX summary text
+ });
+
+ tagText += "}}\n";
+ // XXX summaryTagsAdded.push(summaryText);
+ }
+ else {
+ // {{multiple issues}} is not present and needs to be added
+ Status.info( 'Info', 'Adding supported tags into {{multiple issues}}' );
- groupableTags.sort();
- tagText += '{{multiple issues';
- summaryText += ' {{[[Template:multiple issues|multiple issues]]}} with parameters';
- for( i = 0; i < groupableTags.length; i++ ) {
- tagText += '|' + groupableTags[i] +
- '={{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}';
+ tagsConvertedToMIParams = tagsConvertedToMIParams.concat(groupableTagsToKeep);
- if( i === (groupableTags.length - 1) ) {
- summaryText += ' and';
- } else if ( i < (groupableTags.length - 1) && i > 0 ) {
- summaryText += ',';
+ var groupableTags = groupableTagsToAdd.concat(groupableTagsToKeep);
+ groupableTags.sort();
+
+ tagText += '{{multiple issues';
+ var summaryText = '{{multiple issues}} (with parameters';
+ for( i = 0; i < groupableTags.length; i++ ) {
+ tagText += '|' + groupableTags[i] + '={{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}';
+
+ if( i === (groupableTags.length - 1) ) {
+ summaryText += ' and';
+ } else if ( i < (groupableTags.length - 1) && i > 0 ) {
+ summaryText += ',';
+ }
+ summaryText += ' "' + groupableTags[i] + '"';
}
- summaryText += ' ' + groupableTags[i];
+ tagText += '}}\n';
+ summaryText += ")";
+ summaryTagsAdded.push(summaryText);
}
- tagText += '}}\n';
} else {
- tags = tags.concat( groupableTags );
+ // otherwise, remove {{multiple issues}}, or if there isn't one, just proceed as normal
+ if (miResult) {
+ $.each(miResult[3].split("|").slice(1), function(k, tag) {
+ var paramName = tag.substring(0, tag.indexOf("=")).trim();
+ // normalize param name
+ var normalParamName = Twinkle.tag.article.multipleIssuesParamsToCanonicalNames[paramName];
+ if (!normalParamName) {
+ tags.push(paramName);
+ } else if (params.tagsToRemove.indexOf(normalParamName) === -1) {
+ tags.push(normalParamName);
+ // XXX summary text
+ }
+ });
+ // XXX cross-check against groupableTagsToKeep??
+
+ pageText = pageText.replace(miRegex, "\n");
+ }
+ tags = tags.concat(groupableTagsToAdd);
}
} else {
- // Check for pre-existing tags
+ // Redirect tagging: check for pre-existing tags
for( i = 0; i < params.tags.length; i++ ) {
- tagRe = new RegExp( '(\\{\\{' + params.tags[i] + '(\\||\\}\\}))', 'im' );
+ var tagRe = new RegExp( '(\\{\\{' + params.tags[i] + '(\\||\\}\\}))', 'im' );
if( !tagRe.exec( pageText ) ) {
tags = tags.concat( params.tags[i] );
} else {
- Status.info( 'Info', 'Found {{' + params.tags[i] +
- '}} on the redirect already...excluding' );
+ Status.info( 'Info', 'Found {{' + params.tags[i] + '}} on the redirect already... excluding' );
}
}
}
+ // remove tags that were deselected by the user
+ if (params.tagsToRemove.length + tagsConvertedToMIParams.length > 0) {
+ // search for tags in the first 256 and last 256 bytes of page
+ var pageTextStart = pageText.substring(0, 256);
+ var pageTextEnd = pageText.substring(pageText.length - 257);
+ var regexResult;
+ var generalTagRegex = new RegExp("\\s*(?:((?:\\s*\\{\\{\\s*([^|}]+)\\s*(\\|(?:\\{\\{[^{}]*\\}\\}|[^{}])*)?\\}\\}))\\s*)", "gim")
+ while (regexResult = generalTagRegex.exec(pageTextStart)) {
+ var tagLinkQuery = {
+ action: "query",
+ titles: "Template:" + regexResult[2],
+ redirects: "yes"
+ };
+ //var tagLinkApi = new Wikipedia.api("Fetching page params", tagLinkQuery
+ // synchronicity is a problem
+ }
+
+ $.each(params.tagsToRemove.concat(tagsConvertedToMIParams), function(index, tag) {
+ // capture 1: full tag; capture 2: tag name; capture 3: parameters (including leading pipe)
+ var tagRegex = new RegExp("\\s*(?:((?:\\s*\\{\\{\\s*(" + tag + ")\\s*(\\|(?:\\{\\{[^{}]*\\}\\}|[^{}])*)?\\}\\}))\\s*)", "im");
+ var newPageText = pageText.replace(tagRegex, "\n");
+ if (newPageText === pageText) {
+ if (index >= params.tagToRemove.length) { // it is to be expected that these may not be found...??
+
+ } else {
+
+ }
+ //Status.error("Removing tag {{" + tag + "}}", "Could not find tag on page"); -- silent fail...
+ } else {
+ pageText = newPageText;
+ }
+
+ summaryTagsRemoved.push("{{" + tag + "}}");
+ });
+
+ }
+
tags.sort();
for( i = 0; i < tags.length; i++ ) {
var currentTag = "";
if( tags[i] === 'uncategorized' || tags[i] === 'cat improve' ) {
- pageText += '\n\n{{' + tags[i] +
- '|date={{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}}}';
+ pageText += '\n\n{{' + tags[i] + '|date={{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}}}';
} else {
if( tags[i] === 'globalize' ) {
currentTag += '{{' + params.globalizeSubcategory;
@@ -971,22 +1451,14 @@ Twinkle.tag.callbacks = {
currentTag += Twinkle.tag.mode === 'redirect' ? '}}' : '|date={{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}}}\n';
tagText += currentTag;
}
-
- if ( i > 0 || groupableTags.length > 3 ) {
- if( i === (tags.length - 1) ) {
- summaryText += ' and';
- } else if ( i < (tags.length - 1) ) {
- summaryText += ',';
- }
- }
-
- summaryText += ' {{[[Template:';
+ var summaryText = '{{';
if( tags[i] === 'globalize' ) {
- summaryText += params.globalizeSubcategory + '|' + params.globalizeSubcategory;
+ summaryText += params.globalizeSubcategory;
} else {
- summaryText += tags[i] + '|' + tags[i];
+ summaryText += tags[i];
}
- summaryText += ']]}}';
+ summaryText += '}}';
+ summaryTagsAdded.push(summaryText);
}
if( Twinkle.tag.mode === 'redirect' ) {
@@ -998,8 +1470,41 @@ Twinkle.tag.callbacks = {
pageText = pageText.replace(/^\s*(?:((?:\s*\{\{\s*(?:about|correct title|dablink|distinguish|for|other\s?(?:hurricaneuses|people|persons|places|uses(?:of)?)|redirect(?:-acronym)?|see\s?(?:also|wiktionary)|selfref|the)\d*\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\})+(?:\s*\n)?)\s*)?/i,
"$1" + tagText);
}
- summaryText += ' tag' + ( ( tags.length + ( groupableTags.length > 3 ? 1 : 0 ) ) > 1 ? 's' : '' ) +
- ' to ' + Twinkle.tag.mode + Twinkle.getPref('summaryAd');
+
+ var summaryText = "";
+ switch (summaryTagsRemoved.length) {
+ case 0:
+ break;
+ case 1:
+ summaryText += "Removed " + summaryTagsRemoved[0] + " tag";
+ break;
+ case 2:
+ summaryText += "Removed " + summaryTagsRemoved[0] + " and " + summaryTagsRemoved[1] + " tags";
+ break;
+ default:
+ summaryText += "Removed " + summaryTagsRemoved.slice(0, summaryTagsRemoved.length - 1).join(", ") + " and " +
+ summaryTagsRemoved[summaryTagsRemoved.length - 1] + " tags";
+ break;
+ }
+ if (summaryTagsAdded.length > 0) {
+ summaryText += (summaryTagsRemoved.length > 0) ? "; added " : "Added ";
+ }
+ switch (summaryTagsAdded.length) {
+ case 0:
+ break;
+ case 1:
+ summaryText += summaryTagsAdded[0] + " tag";
+ break;
+ case 2:
+ summaryText += summaryTagsAdded[0] + " and " + summaryTagsAdded[1] + " tags";
+ break;
+ default:
+ summaryText += summaryTagsAdded.slice(0, summaryTagsAdded.length - 1).join(", ") + " and " +
+ summaryTagsAdded[summaryTagsAdded.length - 1] + " tags";
+ break;
+ }
+
+ summaryText += (summaryTagsAdded.length > 0 ? ' to ' : ' from ') + Twinkle.tag.mode + Twinkle.getPref('summaryAd');
pageobj.setPageText(pageText);
pageobj.setEditSummary(summaryText);
@@ -1150,6 +1655,18 @@ Twinkle.tag.callback.evaluate = function friendlytagCallbackEvaluate(e) {
params.group = form.group.checked;
params.globalizeSubcategory = form["articleTags.globalize"] ? form["articleTags.globalize"].value : null;
params.notabilitySubcategory = form["articleTags.notability"] ? form["articleTags.notability"].value : null;
+
+ params.tagsToRemove = [];
+ params.tagsToKeep = [];
+ $.each(Twinkle.tag.foundTags, function(k, v) {
+ var index;
+ if ((index = params.tags.indexOf(v)) === -1) {
+ params.tagsToRemove.push(v);
+ } else {
+ params.tags = params.tags.slice(0, index).concat(params.tags.slice(index + 1)); // remove from list of tags to be added
+ params.tagsToKeep.push(v);
+ }
+ });
break;
case 'file':
params.svgSubcategory = form["imageTags.svgCategory"] ? form["imageTags.svgCategory"].value : null;
@@ -1167,8 +1684,8 @@ Twinkle.tag.callback.evaluate = function friendlytagCallbackEvaluate(e) {
break;
}
- if( !params.tags.length ) {
- alert( 'You must select at least one tag!' );
+ if( !params.tags.length && !params.tagsToRemove.length ) {
+ alert( 'You must select at least one tag to add or remove!' );
return;
}
diff --git a/modules/twinklespeedy.js b/modules/twinklespeedy.js
index ae041fe..ddf7c8d 100644
--- a/modules/twinklespeedy.js
+++ b/modules/twinklespeedy.js
@@ -824,10 +824,12 @@ Twinkle.speedy.callbacks = {
if (params.deleteRedirects) {
var query = {
'action': 'query',
- 'list': 'backlinks',
- 'blfilterredir': 'redirects',
- 'bltitle': mw.config.get('wgPageName'),
- 'bllimit': 5000 // 500 is max for normal users, 5000 for bots and sysops
+ 'prop': 'info',
+ 'generator': 'backlinks',
+ 'gbltitle': mw.config.get('wgPageName'),
+ 'gblfilterredir': 'redirects',
+ 'gbllimit': 5000, // 500 is max for normal users, 5000 for bots and sysops
+ 'intoken': 'delete'
};
var wikipedia_api = new Wikipedia.api( 'getting list of redirects...', query, Twinkle.speedy.callbacks.sysop.deleteRedirectsMain,
new Status( 'Deleting redirects' ) );
@@ -864,18 +866,20 @@ Twinkle.speedy.callbacks = {
},
deleteRedirectsMain: function( apiobj ) {
var xmlDoc = apiobj.getXML();
- var $snapshot = $(xmlDoc).find('backlinks bl');
+ var $snapshot = $(xmlDoc).find('page');
var total = $snapshot.length;
if( !total ) {
+ apiobj.statelem.info("there are no redirects");
return;
}
var statusIndicator = apiobj.statelem;
statusIndicator.status("0%");
- var onsuccess = function( apiobj ) {
+ var onsuccess = function( delapiobj ) {
+ delapiobj.statelem.info("done");
var obj = apiobj.params.obj;
var total = apiobj.params.total;
var now = parseInt( 100 * ++(apiobj.params.current)/total, 10 ) + '%';
@@ -895,10 +899,40 @@ Twinkle.speedy.callbacks = {
params.obj = statusIndicator;
$snapshot.each(function(key, value) {
- var title = $(value).attr('title');
- var page = new Wikipedia.page(title, 'Deleting redirect "' + title + '"');
- page.setEditSummary('[[WP:CSD#G8|G8]]: Redirect to deleted page [[' + mw.config.get('wgPageName') + "]]." + Twinkle.getPref('deletionSummaryAd'));
- page.deletePage(onsuccess);
+ var $page = $(value);
+ var title = $page.attr('title');
+ var status = new Status('Deleting redirect "' + title + '"');
+
+ // this code was unceremoniously copied from morebits.js
+ if ($page.attr('missing') === "") {
+ status.error("Cannot delete the page, because it no longer exists");
+ return true; // continue
+ }
+
+ // extract protection info
+ var $editprot = $page.find('pr[type="edit"]');
+ if ($editprot.length > 0 && $editprot.attr('level') === 'sysop' && !confirm('You are about to delete the fully protected page "' + title +
+ ($editprot.attr('expiry') === 'infinity' ? '" (protected indefinitely)' : ('" (protection expiring ' + $editprot.attr('expiry') + ')')) +
+ '. \n\nClick OK to proceed with the deletion, or Cancel to skip this deletion.')) {
+ status.error("Deletion of fully protected page was aborted.");
+ return;
+ }
+
+ var deleteToken = $page.attr('deletetoken');
+ if (!deleteToken) {
+ status.error("Failed to retrieve delete token.");
+ return;
+ }
+
+ var query = {
+ 'action': 'delete',
+ 'title': title,
+ 'token': deleteToken,
+ 'reason': '[[WP:CSD#G8|G8]]: Redirect to deleted page [[' + mw.config.get('wgPageName') + "]]." + Twinkle.getPref('deletionSummaryAd')
+ };
+
+ var delapi = new Wikipedia.api("sending request...", query, onsuccess, status);
+ delapi.post();
});
}
},
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment