Last active
December 15, 2015 21:49
-
-
Save atlight/5328823 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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