From a84def5b73581fb852c2137d002baa5f04bf5812 Mon Sep 17 00:00:00 2001 From: NovemLinguae Date: Wed, 21 Aug 2024 02:06:58 -0700 Subject: [PATCH 01/18] allow es6 in test files (since they use node) --- .eslintrc.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.eslintrc.json b/.eslintrc.json index f170c0fb..70b2ee43 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -12,6 +12,16 @@ "mw": "writable", "OO": "readonly" }, + "overrides": [ + { + "files": [ + "tests/*" + ], + "parserOptions": { + "ecmaVersion": 6 + } + } + ], "rules": { "camelcase": "off", "eqeqeq": "warn", From bd50277940d506734067ef79ed1c209eea050c0e Mon Sep 17 00:00:00 2001 From: NovemLinguae Date: Wed, 21 Aug 2024 02:07:17 -0700 Subject: [PATCH 02/18] make existing test strings multi-line, for readability --- tests/test-core.js | 77 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/tests/test-core.js b/tests/test-core.js index 61edc6ce..3fa8d89f 100644 --- a/tests/test-core.js +++ b/tests/test-core.js @@ -133,13 +133,19 @@ describe( 'AFCH.addTalkPageBanners', function () { var alreadyHasWPBio = false; var existingWPBioTemplateName = null; var output = AFCH.addTalkPageBanners( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName, existingWikiProjects, alreadyHasWPBio, existingWPBioTemplateName ); - expect( output.talkText ).toBe( '{{subst:WPAFC/article|class=|oldid=592485}}\n\n' ); + expect( output.talkText ).toBe( +`{{subst:WPAFC/article|class=|oldid=592485}} + +` + ); expect( output.countOfWikiProjectsAdded ).toBe( 0 ); expect( output.countOfWikiProjectsRemoved ).toBe( 0 ); } ); it( 'talk page has existing sections', function () { - var talkText = '== Hello ==\nI have a question. Can you help answer it? –[[User:Novem Linguae|\'\'\'Novem Linguae\'\'\']] ([[User talk:Novem Linguae|talk]]) 20:22, 10 April 2024 (UTC)'; + var talkText = +`== Hello == +I have a question. Can you help answer it? –[[User:Novem Linguae|'''Novem Linguae''']] ([[User talk:Novem Linguae|talk]]) 20:22, 10 April 2024 (UTC)`; var newAssessment = ''; var revId = 592485; var isBiography = false; @@ -150,14 +156,22 @@ describe( 'AFCH.addTalkPageBanners', function () { var alreadyHasWPBio = false; var existingWPBioTemplateName = null; var output = AFCH.addTalkPageBanners( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName, existingWikiProjects, alreadyHasWPBio, existingWPBioTemplateName ); - expect( output.talkText ).toBe( '{{subst:WPAFC/article|class=|oldid=592485}}\n\n== Hello ==\nI have a question. Can you help answer it? –[[User:Novem Linguae|\'\'\'Novem Linguae\'\'\']] ([[User talk:Novem Linguae|talk]]) 20:22, 10 April 2024 (UTC)' ); + expect( output.talkText ).toBe( +`{{subst:WPAFC/article|class=|oldid=592485}} + +== Hello == +I have a question. Can you help answer it? –[[User:Novem Linguae|'''Novem Linguae''']] ([[User talk:Novem Linguae|talk]]) 20:22, 10 April 2024 (UTC)` + ); expect( output.countOfWikiProjectsAdded ).toBe( 0 ); expect( output.countOfWikiProjectsRemoved ).toBe( 0 ); } ); // FIXME: unexpected \n between new banners and old banners. https://github.com/wikimedia-gadgets/afc-helper/issues/330 it( 'talk page has existing WikiProject banners', function () { - var talkText = '{{WikiProject Women}}\n{{WikiProject Women\'s sport}}\n{{WikiProject Somalia}}'; + var talkText = +`{{WikiProject Women}} +{{WikiProject Women's sport}} +{{WikiProject Somalia}}`; var newAssessment = ''; var revId = 592507; var isBiography = false; @@ -184,14 +198,23 @@ describe( 'AFCH.addTalkPageBanners', function () { var alreadyHasWPBio = false; var existingWPBioTemplateName = null; var output = AFCH.addTalkPageBanners( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName, existingWikiProjects, alreadyHasWPBio, existingWPBioTemplateName ); - expect( output.talkText ).toBe( '{{subst:WPAFC/article|class=|oldid=592507}}\n\n{{WikiProject Women}}\n{{WikiProject Women\'s sport}}\n{{WikiProject Somalia}}' ); + expect( output.talkText ).toBe( +`{{subst:WPAFC/article|class=|oldid=592507}} + +{{WikiProject Women}} +{{WikiProject Women's sport}} +{{WikiProject Somalia}}` + ); expect( output.countOfWikiProjectsAdded ).toBe( 0 ); expect( output.countOfWikiProjectsRemoved ).toBe( 0 ); } ); // FIXME: the edit summary of 1 WikiProject banner removed is correct, but this doesn't actually remove the WikiProject banner from the talk page. https://github.com/wikimedia-gadgets/afc-helper/issues/329 it( 'remove an existing WikiProject', function () { - var talkText = '{{WikiProject Women}}\n{{WikiProject Women\'s sport}}\n{{WikiProject Somalia}}'; + var talkText = +`{{WikiProject Women}} +{{WikiProject Women's sport}} +{{WikiProject Somalia}}`; var newAssessment = ''; var revId = 592507; var isBiography = false; @@ -219,7 +242,13 @@ describe( 'AFCH.addTalkPageBanners', function () { var alreadyHasWPBio = false; var existingWPBioTemplateName = null; var output = AFCH.addTalkPageBanners( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName, existingWikiProjects, alreadyHasWPBio, existingWPBioTemplateName ); - expect( output.talkText ).toBe( '{{subst:WPAFC/article|class=|oldid=592507}}\n\n{{WikiProject Women}}\n{{WikiProject Women\'s sport}}\n{{WikiProject Somalia}}' ); + expect( output.talkText ).toBe( +`{{subst:WPAFC/article|class=|oldid=592507}} + +{{WikiProject Women}} +{{WikiProject Women's sport}} +{{WikiProject Somalia}}` + ); expect( output.countOfWikiProjectsAdded ).toBe( 0 ); expect( output.countOfWikiProjectsRemoved ).toBe( 1 ); } ); @@ -236,7 +265,14 @@ describe( 'AFCH.addTalkPageBanners', function () { var alreadyHasWPBio = false; var existingWPBioTemplateName = null; var output = AFCH.addTalkPageBanners( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName, existingWikiProjects, alreadyHasWPBio, existingWPBioTemplateName ); - expect( output.talkText ).toBe( '{{subst:WPAFC/article|class=B|oldid=592496}}\n{{WikiProject Biography|living=yes|class=B|listas=Jones, Bob}}\n{{WikiProject Africa|class=B}}\n{{WikiProject Alabama|class=B}}\n\n' ); + expect( output.talkText ).toBe( +`{{subst:WPAFC/article|class=B|oldid=592496}} +{{WikiProject Biography|living=yes|class=B|listas=Jones, Bob}} +{{WikiProject Africa|class=B}} +{{WikiProject Alabama|class=B}} + +` + ); expect( output.countOfWikiProjectsAdded ).toBe( 2 ); expect( output.countOfWikiProjectsRemoved ).toBe( 0 ); } ); @@ -253,7 +289,12 @@ describe( 'AFCH.addTalkPageBanners', function () { var alreadyHasWPBio = false; var existingWPBioTemplateName = null; var output = AFCH.addTalkPageBanners( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName, existingWikiProjects, alreadyHasWPBio, existingWPBioTemplateName ); - expect( output.talkText ).toBe( '{{subst:WPAFC/article|class=|oldid=592496}}\n{{WikiProject Biography|living=no|class=|listas=}}\n\n' ); + expect( output.talkText ).toBe( +`{{subst:WPAFC/article|class=|oldid=592496}} +{{WikiProject Biography|living=no|class=|listas=}} + +` + ); expect( output.countOfWikiProjectsAdded ).toBe( 0 ); expect( output.countOfWikiProjectsRemoved ).toBe( 0 ); } ); @@ -261,7 +302,9 @@ describe( 'AFCH.addTalkPageBanners', function () { // FIXME: is supposed to remove the {{wikiproject biography}} template and report 1 template removed, but does not. code outside of AFCH.addTalkPageBanners() is incorrectly calculating alreadyHasWPBio as false // FIXME: 2 extra line breaks in the output it( 'talk page has {{wikiproject biography}}, and user selects that it\'s not a biography, so should remove {{wikiproject biography}}', function () { - var talkText = '{{wikiproject biography|living=yes|class=B|listas=Jones, Bob}}\n{{WikiProject Somalia}}'; + var talkText = +`{{wikiproject biography|living=yes|class=B|listas=Jones, Bob}} +{{WikiProject Somalia}}`; var newAssessment = ''; var revId = 592496; var isBiography = false; @@ -283,7 +326,12 @@ describe( 'AFCH.addTalkPageBanners', function () { var alreadyHasWPBio = false; var existingWPBioTemplateName = null; var output = AFCH.addTalkPageBanners( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName, existingWikiProjects, alreadyHasWPBio, existingWPBioTemplateName ); - expect( output.talkText ).toBe( '{{subst:WPAFC/article|class=|oldid=592496}}\n\n{{wikiproject biography|living=yes|class=B|listas=Jones, Bob}}\n{{WikiProject Somalia}}' ); + expect( output.talkText ).toBe( +`{{subst:WPAFC/article|class=|oldid=592496}} + +{{wikiproject biography|living=yes|class=B|listas=Jones, Bob}} +{{WikiProject Somalia}}` + ); expect( output.countOfWikiProjectsAdded ).toBe( 0 ); expect( output.countOfWikiProjectsRemoved ).toBe( 0 ); } ); @@ -300,7 +348,12 @@ describe( 'AFCH.addTalkPageBanners', function () { var alreadyHasWPBio = false; var existingWPBioTemplateName = null; var output = AFCH.addTalkPageBanners( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName, existingWikiProjects, alreadyHasWPBio, existingWPBioTemplateName ); - expect( output.talkText ).toBe( '{{subst:WPAFC/article|class=disambig|oldid=592681}}\n{{WikiProject Disambiguation|class=disambig}}\n\n' ); + expect( output.talkText ).toBe( +`{{subst:WPAFC/article|class=disambig|oldid=592681}} +{{WikiProject Disambiguation|class=disambig}} + +` + ); expect( output.countOfWikiProjectsAdded ).toBe( 1 ); expect( output.countOfWikiProjectsRemoved ).toBe( 0 ); } ); From 5f1f3e876425c4437b9db697ce207726e0bffe47 Mon Sep 17 00:00:00 2001 From: NovemLinguae Date: Wed, 21 Aug 2024 02:46:14 -0700 Subject: [PATCH 03/18] add banner shell if needed --- src/modules/core.js | 18 +++++++++++++++++- tests/test-core.js | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/modules/core.js b/src/modules/core.js index 67fe61ec..9cb73d62 100644 --- a/src/modules/core.js +++ b/src/modules/core.js @@ -1708,10 +1708,26 @@ // (e.g. pages in `Draft:` namespace with discussion) talkText = talkTextPrefix + '\n\n' + talkText; + // Add banner shell if needed + var banners = talkText.match( /{{(?:wikiproject[^}]+}}|subst:wpafc)/gi ); + // https://en.wikipedia.org/wiki/Special:WhatLinksHere?target=Template%3AWikiProject+banner+shell&namespace=&hidetrans=1&hidelinks=1 + var bannerShellDetectionRegex = /{{(?:WikiProject banner shell|WikiProjectBanners|WikiProject Banners|WPB|WPBS|WikiProject cooperation shell|Wikiprojectbannershell|WikiProject Banner Shell|Wpb|WPBannerShell|Wpbs|Wikiprojectbanners|WP Banner Shell|WP banner shell|Bannershell|Wikiproject banner shell|WIkiProjectBanner Shell|WikiProjectBannerShell|WikiProject BannerShell|Coopshell|WikiprojectBannerShell|WikiProject Shell|Scope shell|Project shell|WikiProject shell|WikiProject banner|Wpbannershell|Multiple wikiprojects|Wikiproject banner holder|Project banner holder|WikiProject banner shell\/test1|Article assessment|WikiProject bannershell)/i; + var hasBannerShell = talkText.match( bannerShellDetectionRegex ); + if ( banners.length > 1 && !hasBannerShell ) { + var bannerShellStart = '{{WikiProject banner shell|'; + var bannerShellEnd = '}}'; + var firstBanner = banners[ 0 ]; + var lastBanner = banners.slice( -1 )[ 0 ]; + talkText = talkText.replace( firstBanner, bannerShellStart + '\n' + firstBanner ); + talkText = talkText.replace( lastBanner, lastBanner + '\n' + bannerShellEnd ); + } + return { talkText: talkText, countOfWikiProjectsAdded: wikiProjectsToAdd.length, - countOfWikiProjectsRemoved: wikiProjectsToRemove.length + countOfWikiProjectsRemoved: wikiProjectsToRemove.length, + // adding this param mainly for unit tests, so we can test how well the banner detection algorithm works + bannerCount: banners.length }; }, diff --git a/tests/test-core.js b/tests/test-core.js index 3fa8d89f..f19b4863 100644 --- a/tests/test-core.js +++ b/tests/test-core.js @@ -140,6 +140,7 @@ describe( 'AFCH.addTalkPageBanners', function () { ); expect( output.countOfWikiProjectsAdded ).toBe( 0 ); expect( output.countOfWikiProjectsRemoved ).toBe( 0 ); + expect( output.bannerCount ).toBe( 1 ); } ); it( 'talk page has existing sections', function () { @@ -164,6 +165,7 @@ I have a question. Can you help answer it? –[[User:Novem Linguae|'''Novem Linguae''']] ([[User talk:Novem Linguae|talk]]) 20:22, 10 April 2024 (UTC)` From 9dd2b4c312e6eff9358966a31282de01ccb3ed85 Mon Sep 17 00:00:00 2001 From: NovemLinguae Date: Thu, 22 Aug 2024 08:27:20 -0700 Subject: [PATCH 08/18] handle existing banner shell --- src/modules/core.js | 28 ++++++++++++++++++++------- tests/test-core.js | 47 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/src/modules/core.js b/src/modules/core.js index 443380ca..44c927a3 100644 --- a/src/modules/core.js +++ b/src/modules/core.js @@ -1708,21 +1708,35 @@ // (e.g. pages in `Draft:` namespace with discussion) talkText = talkTextPrefix + '\n\n' + talkText; - // Add banner shell, always. This is easier than maintaining 2 code paths: a code path for 1 banner and a code path for 2+ banners. + // If present, delete WikiProject banner shell. We'll re-add it later. + var bannerShellTemplates = 'WikiProject banner shell|WikiProjectBanners|WikiProject Banners|WPB|WPBS|WikiProject cooperation shell|Wikiprojectbannershell|WikiProject Banner Shell|Wpb|WPBannerShell|Wpbs|Wikiprojectbanners|WP Banner Shell|WP banner shell|Bannershell|Wikiproject banner shell|WIkiProjectBanner Shell|WikiProjectBannerShell|WikiProject BannerShell|Coopshell|WikiprojectBannerShell|WikiProject Shell|Scope shell|Project shell|WikiProject shell|WikiProject banner|Wpbannershell|Multiple wikiprojects|Wikiproject banner holder|Project banner holder|WikiProject banner shell\\/test1|Article assessment|WikiProject bannershell'; + var bannerShellDetectionRegex = new RegExp( '{{(?:' + bannerShellTemplates + ')[^\n]*\n', 'i' ); + var hasBannerShell = talkText.match( bannerShellDetectionRegex ); + var bannerShellWasDeleted = false; + if ( hasBannerShell ) { + talkText = talkText.replace( hasBannerShell, '' ); + bannerShellWasDeleted = true; + } + + // Add WikiProject banner shell, always. This is easier than maintaining 2 code paths: a code path for 1 banner and a code path for 2+ banners. var banners = talkText.match( /{{(?:wikiproject|subst:wpafc\/article|football)[^}]+}}/gi ); // https://en.wikipedia.org/wiki/Special:WhatLinksHere?target=Template%3AWikiProject+banner+shell&namespace=&hidetrans=1&hidelinks=1 - var bannerShellDetectionRegex = /{{(?:WikiProject banner shell|WikiProjectBanners|WikiProject Banners|WPB|WPBS|WikiProject cooperation shell|Wikiprojectbannershell|WikiProject Banner Shell|Wpb|WPBannerShell|Wpbs|Wikiprojectbanners|WP Banner Shell|WP banner shell|Bannershell|Wikiproject banner shell|WIkiProjectBanner Shell|WikiProjectBannerShell|WikiProject BannerShell|Coopshell|WikiprojectBannerShell|WikiProject Shell|Scope shell|Project shell|WikiProject shell|WikiProject banner|Wpbannershell|Multiple wikiprojects|Wikiproject banner holder|Project banner holder|WikiProject banner shell\/test1|Article assessment|WikiProject bannershell)/i; - var hasBannerShell = talkText.match( bannerShellDetectionRegex ); + bannerShellDetectionRegex = new RegExp( '{{(?:' + bannerShellTemplates + ')', 'i' ); + hasBannerShell = talkText.match( bannerShellDetectionRegex ); if ( banners.length >= 1 && !hasBannerShell ) { var bannerShellStart = '{{WikiProject banner shell|'; - var bannerShellEnd = '}}'; var firstBanner = banners[ 0 ]; - var lastBanner = banners.slice( -1 )[ 0 ]; talkText = talkText.replace( firstBanner, bannerShellStart + '\n' + firstBanner ); - talkText = talkText.replace( lastBanner, lastBanner + '\n' + bannerShellEnd ); + + // If we deleted the banner shell above, we didn't delete its closing }}. Skip adding it here. Just recycle it. + if ( !bannerShellWasDeleted ) { + var bannerShellEnd = '}}'; + var lastBanner = banners.slice( -1 )[ 0 ]; + talkText = talkText.replace( lastBanner, lastBanner + '\n' + bannerShellEnd ); + } } - // If banner shell is present, comply with [[WP:PIQA]]. Add the class only to the banner shell. Delete any other class parameters. + // Comply with [[WP:PIQA]]. Add the |class= only to the banner shell. Delete all other class parameters (e.g. from WikiProject banners) hasBannerShell = talkText.match( bannerShellDetectionRegex ); if ( hasBannerShell ) { // delete all |class= from the entire talk page diff --git a/tests/test-core.js b/tests/test-core.js index a72e7039..bf51a7f7 100644 --- a/tests/test-core.js +++ b/tests/test-core.js @@ -209,6 +209,53 @@ I have a question. Can you help answer it? –[[User:Novem Linguae| Date: Thu, 22 Aug 2024 08:51:25 -0700 Subject: [PATCH 10/18] add test case --- tests/test-core.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/test-core.js b/tests/test-core.js index 742bd9fc..3bd226f5 100644 --- a/tests/test-core.js +++ b/tests/test-core.js @@ -264,6 +264,44 @@ I have a question. Can you help answer it? –[[User:Novem Linguae| Date: Thu, 22 Aug 2024 09:20:50 -0700 Subject: [PATCH 13/18] tweak comments --- tests/test-core.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test-core.js b/tests/test-core.js index f29d327b..0388e3ff 100644 --- a/tests/test-core.js +++ b/tests/test-core.js @@ -230,7 +230,6 @@ I have a question. Can you help answer it? –[[User:Novem Linguae| is a chips interface called jquery.chosen. Note that if there are existing WikiProject banners on the page, the form will auto-add those to the "Add WikiProjects" part of the form when it first loads. * @param {string} lifeStatus Value of "Is the subject alive?" dropdown list ("unknown", "living", "dead") * @param {string} subjectName Value of the "Subject name (last, first)" text input, or "" if blank - * @param {Array} existingWikiProjects An array of associative arrays. The associative arrays contain the keys {string} displayName (example: Somalia), {string} templateName (example: WikiProject Somalia), and {boolean} alreadyOnPage + * @param {Array} existingWikiProjects An array of associative arrays. The associative arrays contain the keys: + * 1) {string} displayName (example: Somalia), + * 2) {string} templateName (example: WikiProject Somalia), + * 3) {boolean} alreadyOnPage * @param {boolean} alreadyHasWPBio * @param {null} existingWPBioTemplateName * @return {Object} { {string} talkText, {number} countOfWikiProjectsAdded, {number} countOfWikiProjectsRemoved } */ addTalkPageBanners: function ( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName, existingWikiProjects, alreadyHasWPBio, existingWPBioTemplateName ) { - var talkTextPrefix = ''; + // build an array of all banners already on page + var bannerTemplates = 'wikiproject (?!banner)|football|oka'; + var bannerTemplateRegEx = new RegExp( '{{(?:' + bannerTemplates + ')[^}]+}}', 'gi' ); + var banners = talkText.match( bannerTemplateRegEx ) || []; - // Add the AFC banner - talkTextPrefix += '{{subst:WPAFC/article|class=' + newAssessment + - ( revId ? '|oldid=' + revId : '' ) + '}}'; + // log the count of existing banners. used to calculate how many banners added and removed in the edit summary + var originalBannerCount = banners.length; - // Add biography banner if specified - if ( isBiography ) { - // Ensure we don't have duplicate biography tags - AFCH.removeFromArray( newWikiProjects, 'WikiProject Biography' ); + // delete all banners already on page + banners.forEach( function ( v ) { + talkText = talkText.replace( v, '' ); + } ); - talkTextPrefix += ( '\n{{WikiProject Biography|living=' + - ( lifeStatus !== 'unknown' ? ( lifeStatus === 'living' ? 'yes' : 'no' ) : '' ) + - '|class=' + newAssessment + '|listas=' + subjectName + '}}' ); - } + // delete shell already on page + var bannerShellTemplates = 'WikiProject banner shell|WikiProjectBanners|WikiProject Banners|WPB|WPBS|WikiProject cooperation shell|Wikiprojectbannershell|WikiProject Banner Shell|Wpb|WPBannerShell|Wpbs|Wikiprojectbanners|WP Banner Shell|WP banner shell|Bannershell|Wikiproject banner shell|WIkiProjectBanner Shell|WikiProjectBannerShell|WikiProject BannerShell|Coopshell|WikiprojectBannerShell|WikiProject Shell|Scope shell|Project shell|WikiProject shell|WikiProject banner|Wpbannershell|Multiple wikiprojects|Wikiproject banner holder|Project banner holder|WikiProject banner shell\\/test1|Article assessment|WikiProject bannershell'; + var bannerShellRegEx = new RegExp( '{{(?:' + bannerShellTemplates + ')[^}]*}}', 'is' ); + talkText = talkText.replace( bannerShellRegEx, '' ); - // Add disambiguation banner if needed - if ( newAssessment === 'disambig' && - $.inArray( 'WikiProject Disambiguation', newWikiProjects ) === -1 ) { - newWikiProjects.push( 'WikiProject Disambiguation' ); - } + // trim + talkText = talkText.trim(); - // Add and remove WikiProjects - /** @member {Array} */ - var wikiProjectsToAdd = newWikiProjects.filter( function ( newTemplateName ) { - return !existingWikiProjects.some( function ( existingTplObj ) { - return existingTplObj.templateName === newTemplateName; - } ); - } ); - /** @member {Array} */ - var wikiProjectsToRemove = existingWikiProjects.filter( function ( existingTplObj ) { - return !newWikiProjects.some( function ( newTemplateName ) { - return existingTplObj.templateName === newTemplateName; - } ); - } ).map( function ( templateObj ) { - return templateObj.realTemplateName || templateObj.templateName; - } ); - if ( alreadyHasWPBio && !isBiography ) { - wikiProjectsToRemove.push( existingWPBioTemplateName || 'wikiproject biography' ); - } + // add AFC banner to array + banners.push( + '{{subst:WPAFC/article' + + ( revId ? '|oldid=' + revId : '' ) + + '}}' + ); + // AFCH doesn't count the AFC banner, biography banner, and disambiguation banner towards the "added banners" count in the edit summary + originalBannerCount++; - $.each( wikiProjectsToAdd, function ( _index, templateName ) { - talkTextPrefix += '\n{{' + templateName + '|class=' + newAssessment + '}}'; + // delete existing biography banner. when accepting, reviewer is forced to choose if it's a biography or not, so we'll add (or not add) our own biography banner later + banners = banners.filter( function ( value ) { + return !value.match( /^{{WikiProject Biography/i ); } ); - $.each( wikiProjectsToRemove, function ( _index, templateName ) { - // Regex from https://stackoverflow.com/a/5306111/1757964 - var sanitizedTemplateName = templateName.replace( /[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&' ); - talkText = talkText.replace( new RegExp( '\\n?\\{\\{\\s*' + sanitizedTemplateName + '\\s*.+?\\}\\}', 'is' ), '' ); - } ); - - // We prepend the text so that talk page content is not removed - // (e.g. pages in `Draft:` namespace with discussion) - talkText = talkTextPrefix + '\n\n' + talkText; - // If present, delete WikiProject banner shell. We'll re-add it later. - var bannerShellTemplates = 'WikiProject banner shell|WikiProjectBanners|WikiProject Banners|WPB|WPBS|WikiProject cooperation shell|Wikiprojectbannershell|WikiProject Banner Shell|Wpb|WPBannerShell|Wpbs|Wikiprojectbanners|WP Banner Shell|WP banner shell|Bannershell|Wikiproject banner shell|WIkiProjectBanner Shell|WikiProjectBannerShell|WikiProject BannerShell|Coopshell|WikiprojectBannerShell|WikiProject Shell|Scope shell|Project shell|WikiProject shell|WikiProject banner|Wpbannershell|Multiple wikiprojects|Wikiproject banner holder|Project banner holder|WikiProject banner shell\\/test1|Article assessment|WikiProject bannershell'; - var bannerShellDetectionRegex = new RegExp( '{{(?:' + bannerShellTemplates + ')[^\n]*\n', 'i' ); - var hasBannerShell = talkText.match( bannerShellDetectionRegex ); - var bannerShellWasDeleted = false; - if ( hasBannerShell ) { - talkText = talkText.replace( hasBannerShell, '' ); - bannerShellWasDeleted = true; + // add biography banner to array + if ( isBiography ) { + banners.push( + '{{WikiProject Biography|living=' + + ( lifeStatus !== 'unknown' ? ( lifeStatus === 'living' ? 'yes' : 'no' ) : '' ) + + '|listas=' + subjectName + + '}}' + ); + originalBannerCount++; } - // Add WikiProject banner shell, always. This is easier than maintaining 2 code paths: a code path for 1 banner and a code path for 2+ banners. - var banners = talkText.match( /{{(?:wikiproject|subst:wpafc\/article|football|oka)[^}]+}}/gi ); - // https://en.wikipedia.org/wiki/Special:WhatLinksHere?target=Template%3AWikiProject+banner+shell&namespace=&hidetrans=1&hidelinks=1 - bannerShellDetectionRegex = new RegExp( '{{(?:' + bannerShellTemplates + ')', 'i' ); - hasBannerShell = talkText.match( bannerShellDetectionRegex ); - if ( banners.length >= 1 && !hasBannerShell ) { - var bannerShellStart = '{{WikiProject banner shell|'; - var firstBanner = banners[ 0 ]; - talkText = talkText.replace( firstBanner, bannerShellStart + '\n' + firstBanner ); - - // If we deleted the banner shell above, we didn't delete its closing }}. Skip adding it here. Just recycle it. - if ( !bannerShellWasDeleted ) { - var bannerShellEnd = '}}'; - var lastBanner = banners.slice( -1 )[ 0 ]; - talkText = talkText.replace( lastBanner, lastBanner + '\n' + bannerShellEnd ); - } + // add disambiguation banner to array + if ( newAssessment === 'disambig' ) { + banners.push( '{{WikiProject Disambiguation}}' ); + originalBannerCount++; } - // Comply with [[WP:PIQA]]. Add the |class= only to the banner shell. Delete all other class parameters (e.g. from WikiProject banners) - hasBannerShell = talkText.match( bannerShellDetectionRegex ); - if ( hasBannerShell ) { - // delete all |class= from the entire talk page - talkText = talkText.replace( /[\n|}]class\s*=\s*[^\n|}]*([\n|}])/g, '$1' ); - // add |class= to the banner shell only - if ( newAssessment ) { - talkText = talkText.replace( bannerShellDetectionRegex, '$&|class=' + newAssessment ); - } + // add banners selected in UI to array + for ( var key in newWikiProjects ) { + banners.push( '{{' + newWikiProjects[ key ] + '}}' ); } - // Remove extra line breaks between banners - talkText = talkText.replace( /\}\}\n{2,}\{\{/g, '}}\n{{' ); + // remove duplicate banners, case insensitive + banners = AFCH.removeDuplicateBanners( banners ); + + // delete |class= from banners in array + banners = banners.map( function ( value ) { + return value.replace( /\s*\|\s*class\s*=\s*[^|}]*([\n|}])/, '$1' ); + } ); + + // Convert array back to wikitext and append to top of talk page. + // Always add a shell even if it's just wrapping one banner, for code simplification reasons. + // Add |class= to shell. + talkText = '{{WikiProject banner shell' + + ( newAssessment ? '|class=' + newAssessment : '' ) + + '|\n' + + banners.join( '\n' ) + + '\n}}\n' + + talkText; + + // add an extra line break between the last template and the first heading + talkText = talkText.replace( /}}\n==/, '}}\n\n==' ); + + // trim. makes unit tests more stable + talkText = talkText.trim(); return { + // what to write to the talk page talkText: talkText, - countOfWikiProjectsAdded: wikiProjectsToAdd.length, - countOfWikiProjectsRemoved: wikiProjectsToRemove.length, - // adding this param for unit tests, so we can test that the banner detection algorithm works - bannerCount: banners.length + // used in edit summary. the -1 is because the AFC banner should be ignored + countOfWikiProjectsAdded: banners.length - originalBannerCount, + countOfWikiProjectsRemoved: 0, + // used by unit tests + countOfWikiProjects: banners.length }; }, + /** + * In an array of templates, remove duplicate templates, case insensitive. + * + * @param {Array} banners [ '{{WikiProject Australia}}', {{wikiproject australia}}', '{{WikiProject Australia|class=B}}' ] + * @return {Array} banners [ '{{WikiProject Australia}}' ] + */ + removeDuplicateBanners: function ( banners ) { + var uniqueBanners = []; + var bannerMap = {}; + banners.forEach( function ( banner ) { + var bannerKey = banner.toLowerCase().match( /{{[^|}]+/ )[ 0 ]; + if ( !bannerMap[ bannerKey ] ) { + uniqueBanners.push( banner ); + bannerMap[ bannerKey ] = true; + } + } ); + return uniqueBanners; + }, + /** * Returns the relative time that has elapsed between an oldDate and a nowDate * diff --git a/tests/test-core.js b/tests/test-core.js index 0388e3ff..5d152465 100644 --- a/tests/test-core.js +++ b/tests/test-core.js @@ -137,13 +137,11 @@ describe( 'AFCH.addTalkPageBanners', function () { expect( output.talkText ).toBe( `{{WikiProject banner shell| {{subst:WPAFC/article|oldid=592485}} -}} - -` +}}` ); expect( output.countOfWikiProjectsAdded ).toBe( 0 ); expect( output.countOfWikiProjectsRemoved ).toBe( 0 ); - expect( output.bannerCount ).toBe( 1 ); + expect( output.countOfWikiProjects ).toBe( 1 ); } ); it( 'talk page has existing sections', function () { @@ -170,7 +168,7 @@ I have a question. Can you help answer it? –[[User:Novem Linguae| 0 ) { - summary += ', adding ' + results.countOfWikiProjectsAdded + - ' WikiProject banner' + ( ( results.countOfWikiProjectsAdded === 1 ) ? '' : 's' ); - } - if ( results.countOfWikiProjectsRemoved > 0 ) { - summary += ', removing ' + results.countOfWikiProjectsRemoved + - ' WikiProject banner' + ( ( results.countOfWikiProjectsRemoved === 1 ) ? '' : 's' ); - } + var summary = 'Placing [[Wikipedia:Articles for creation|Articles for creation]] banner, and possibly other banners'; if ( comments && comments.length > 0 ) { talkText = talkText.trim() + '\n\n== Comments left by AfC reviewers ==\n' + comments.join( '\n\n' ); diff --git a/tests/test-core.js b/tests/test-core.js index 5d152465..cdf941f8 100644 --- a/tests/test-core.js +++ b/tests/test-core.js @@ -139,9 +139,7 @@ describe( 'AFCH.addTalkPageBanners', function () { {{subst:WPAFC/article|oldid=592485}} }}` ); - expect( output.countOfWikiProjectsAdded ).toBe( 0 ); - expect( output.countOfWikiProjectsRemoved ).toBe( 0 ); - expect( output.countOfWikiProjects ).toBe( 1 ); + expect( output.wikiProjectBannerCount ).toBe( 1 ); } ); it( 'talk page has existing sections', function () { @@ -166,9 +164,7 @@ I have a question. Can you help answer it? –[[User:Novem Linguae|'''Novem Linguae''']] ([[User talk:Novem Linguae|talk]]) 20:22, 10 April 2024 (UTC)` ); - expect( output.countOfWikiProjectsAdded ).toBe( 0 ); - expect( output.countOfWikiProjectsRemoved ).toBe( 0 ); - expect( output.countOfWikiProjects ).toBe( 1 ); + expect( output.wikiProjectBannerCount ).toBe( 1 ); } ); it( 'talk page has existing templates, WikiProject banners on top', function () { @@ -193,9 +189,7 @@ I have a question. Can you help answer it? –[[User:Novem Linguae| is a chips interface called jquery.chosen. Note that if there are existing WikiProject banners on the page, the form will auto-add those to the "Add WikiProjects" part of the form when it first loads. * @param {string} lifeStatus Value of "Is the subject alive?" dropdown list ("unknown", "living", "dead") * @param {string} subjectName Value of the "Subject name (last, first)" text input, or "" if blank - * @param {Array} existingWikiProjects An array of associative arrays. The associative arrays contain the keys: - * 1) {string} displayName (example: Somalia), - * 2) {string} templateName (example: WikiProject Somalia), - * 3) {boolean} alreadyOnPage - * @param {boolean} alreadyHasWPBio - * @param {null} existingWPBioTemplateName * @return {Object} * 1) {string} talkText * 2) {number} wikiProjectBannerCount */ - addTalkPageBanners: function ( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName, existingWikiProjects, alreadyHasWPBio, existingWPBioTemplateName ) { + addTalkPageBanners: function ( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName ) { // build an array of all banners already on page var bannerTemplates = 'wikiproject (?!banner)|football|oka'; var bannerTemplateRegEx = new RegExp( '{{(?:' + bannerTemplates + ')[^}]+}}', 'gi' ); @@ -1674,7 +1668,7 @@ var bannerShellRegEx = new RegExp( '{{(?:' + bannerShellTemplates + ')[^}]*}}', 'is' ); talkText = talkText.replace( bannerShellRegEx, '' ); - // trim + // trim. makes unit tests more stable talkText = talkText.trim(); // add AFC banner to array diff --git a/src/modules/submissions.js b/src/modules/submissions.js index e7089fc5..5aa81875 100644 --- a/src/modules/submissions.js +++ b/src/modules/submissions.js @@ -2395,10 +2395,7 @@ data.isBiography, data.newWikiProjects, data.lifeStatus, - data.subjectName, - data.existingWikiProjects, - data.alreadyHasWPBio, - data.existingWPBioTemplateName + data.subjectName ); talkText = results.talkText; diff --git a/tests/test-core.js b/tests/test-core.js index cdf941f8..e480a9c8 100644 --- a/tests/test-core.js +++ b/tests/test-core.js @@ -130,10 +130,7 @@ describe( 'AFCH.addTalkPageBanners', function () { var newWikiProjects = []; var lifeStatus = 'unknown'; var subjectName = ''; - var existingWikiProjects = []; - var alreadyHasWPBio = false; - var existingWPBioTemplateName = null; - var output = AFCH.addTalkPageBanners( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName, existingWikiProjects, alreadyHasWPBio, existingWPBioTemplateName ); + var output = AFCH.addTalkPageBanners( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName ); expect( output.talkText ).toBe( `{{WikiProject banner shell| {{subst:WPAFC/article|oldid=592485}} @@ -152,10 +149,7 @@ I have a question. Can you help answer it? –[[User:Novem Linguae| is a chips interface called jquery.chosen. Note that if there are existing WikiProject banners on the page, the form will auto-add those to the "Add WikiProjects" part of the form when it first loads. * @param {string} lifeStatus Value of "Is the subject alive?" dropdown list ("unknown", "living", "dead") * @param {string} subjectName Value of the "Subject name (last, first)" text input, or "" if blank - * @return {Object} - * 1) {string} talkText - * 2) {number} wikiProjectBannerCount + * @return {Object} wikicode */ - addTalkPageBanners: function ( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName ) { + addTalkPageBanners: function ( wikicode, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName ) { // build an array of all banners already on page var bannerTemplates = 'wikiproject (?!banner)|football|oka'; var bannerTemplateRegEx = new RegExp( '{{(?:' + bannerTemplates + ')[^}]+}}', 'gi' ); - var banners = talkText.match( bannerTemplateRegEx ) || []; + var banners = wikicode.match( bannerTemplateRegEx ) || []; // delete all banners already on page banners.forEach( function ( v ) { - talkText = talkText.replace( v, '' ); + wikicode = wikicode.replace( v, '' ); } ); // delete shell already on page var bannerShellTemplates = 'WikiProject banner shell|WikiProjectBanners|WikiProject Banners|WPB|WPBS|WikiProject cooperation shell|Wikiprojectbannershell|WikiProject Banner Shell|Wpb|WPBannerShell|Wpbs|Wikiprojectbanners|WP Banner Shell|WP banner shell|Bannershell|Wikiproject banner shell|WIkiProjectBanner Shell|WikiProjectBannerShell|WikiProject BannerShell|Coopshell|WikiprojectBannerShell|WikiProject Shell|Scope shell|Project shell|WikiProject shell|WikiProject banner|Wpbannershell|Multiple wikiprojects|Wikiproject banner holder|Project banner holder|WikiProject banner shell\\/test1|Article assessment|WikiProject bannershell'; var bannerShellRegEx = new RegExp( '{{(?:' + bannerShellTemplates + ')[^}]*}}', 'is' ); - talkText = talkText.replace( bannerShellRegEx, '' ); + wikicode = wikicode.replace( bannerShellRegEx, '' ); // trim. makes unit tests more stable - talkText = talkText.trim(); + wikicode = wikicode.trim(); // add AFC banner to array banners.push( @@ -1714,25 +1712,20 @@ // Convert array back to wikitext and append to top of talk page. // Always add a shell even if it's just wrapping one banner, for code simplification reasons. // Add |class= to shell. - talkText = '{{WikiProject banner shell' + + wikicode = '{{WikiProject banner shell' + ( newAssessment ? '|class=' + newAssessment : '' ) + '|\n' + banners.join( '\n' ) + '\n}}\n' + - talkText; + wikicode; // add an extra line break between the last template and the first heading - talkText = talkText.replace( /}}\n==/, '}}\n\n==' ); + wikicode = wikicode.replace( /}}\n==/, '}}\n\n==' ); // trim. makes unit tests more stable - talkText = talkText.trim(); + wikicode = wikicode.trim(); - return { - // what to write to the talk page - talkText: talkText, - // used by unit tests - wikiProjectBannerCount: banners.length - }; + return wikicode; }, /** diff --git a/src/modules/submissions.js b/src/modules/submissions.js index 5aa81875..afd909f9 100644 --- a/src/modules/submissions.js +++ b/src/modules/submissions.js @@ -2388,7 +2388,7 @@ // --------- talkPage.getText().done( function ( talkText ) { - var results = AFCH.addTalkPageBanners( + talkText = AFCH.addTalkPageBanners( talkText, data.newAssessment, afchPage.additionalData.revId, @@ -2397,7 +2397,6 @@ data.lifeStatus, data.subjectName ); - talkText = results.talkText; var summary = 'Placing [[Wikipedia:Articles for creation|Articles for creation]] banner, and possibly other banners'; diff --git a/tests/test-core.js b/tests/test-core.js index 883fe0ea..23baf169 100644 --- a/tests/test-core.js +++ b/tests/test-core.js @@ -123,24 +123,23 @@ describe( 'AFCH.removeEmptySectionAtEnd', function () { describe( 'AFCH.addTalkPageBanners', function () { it( 'talk page is blank', function () { - var talkText = ''; + var wikicode = ''; var newAssessment = ''; var revId = 592485; var isBiography = false; var newWikiProjects = []; var lifeStatus = 'unknown'; var subjectName = ''; - var output = AFCH.addTalkPageBanners( talkText, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName ); - expect( output.talkText ).toBe( + var output = AFCH.addTalkPageBanners( wikicode, newAssessment, revId, isBiography, newWikiProjects, lifeStatus, subjectName ); + expect( output ).toBe( `{{WikiProject banner shell| {{subst:WPAFC/article|oldid=592485}} }}` ); - expect( output.wikiProjectBannerCount ).toBe( 1 ); } ); it( 'talk page has existing sections', function () { - var talkText = + var wikicode = `== Hello == I have a question. Can you help answer it? –[[User:Novem Linguae|'''Novem Linguae''']] ([[User talk:Novem Linguae|talk]]) 20:22, 10 April 2024 (UTC)`; var newAssessment = ''; @@ -149,8 +148,8 @@ I have a question. Can you help answer it? –[[User:Novem Linguae|'''Novem Linguae''']] ([[User talk:Novem Linguae|talk]]) 20:22, 10 April 2024 (UTC)` ); - expect( output.wikiProjectBannerCount ).toBe( 1 ); } ); it( 'talk page has existing templates, WikiProject banners on top', function () { - var talkText = + var wikicode = `{{WikiProject Women}} {{translated page|ar|بحيرة كناو|version=|small=no|insertversion=|section=}} `; @@ -172,19 +170,18 @@ I have a question. Can you help answer it? –[[User:Novem Linguae|